Compare commits

...

12 Commits

Author SHA1 Message Date
Neels Hofmeyr
2c42f44d7e add libosmo-sdp: add sdp_msg.h,.c
Change-Id: Id53c2c6eea3726f63a1399ae985f8aa3344e32c8
2025-01-14 14:01:31 +01:00
Neels Hofmeyr
53877bfb8e add libosmo-sdp: add codec_list_is_empty()
Change-Id: I5e80f5737f22dc81ac16448fc59937330d24432e
2025-01-14 14:01:31 +01:00
Neels Hofmeyr
aca4ad4cce add libosmo-sdp: add codec_list_by_payload_type
Change-Id: I7a09b0e669d787a1ac0ec7ed7541c107fd0f32db
2025-01-14 14:01:31 +01:00
Neels Hofmeyr
46c3882f75 add libosmo-sdp: add codec_list_intersection
Change-Id: Ie57e4384854a1152dccf9577fdb5f046a1f17e0f
2025-01-14 14:01:31 +01:00
Neels Hofmeyr
5f8bad4b21 add libosmo-sdp: codec_list_cmp()
Change-Id: I4a2087c3c03ef11df03c00a14953b0d0b6f977f3
2025-01-14 14:01:31 +01:00
Neels Hofmeyr
35357e80ee add libosmo-sdp: codec_list_first()
Change-Id: I43653820d8b4d07bf5d7ed25b6172fec26eb2cdf
2025-01-14 14:01:31 +01:00
Neels Hofmeyr
6c09836faa add libosmo-sdp: codec_list_to_str()
Change-Id: I8a421ff8280db3f1ec399832f1a86c515a0888af
2025-01-14 14:01:31 +01:00
Neels Hofmeyr
c43d76dc4d add libosmo-sdp: osmo_sdp_codec_list.h,c, first part
Change-Id: If170566c666c4f4010091bc90912b13a12f77de8
2025-01-14 14:01:31 +01:00
Neels Hofmeyr
0051a29df9 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
2025-01-14 14:01:31 +01:00
Neels Hofmeyr
ef5f1d3d7b add libosmo-sdp: fmtp_amr_match()
Change-Id: I0fb77c022c4fb195ba45631dda9cbd2215c02510
2025-01-14 14:01:31 +01:00
Neels Hofmeyr
c74c3d02c8 add libosmo-sdp [1/n] add fmtp.h, sdp_strings.h
Add new noinst-library libosmo-sdp.la.

It will be used by
- osmo-mgw
- libosmo-mgcp-client
- callers of libosmo-mgcp-client may use SDP message parsing (osmo-msc)

The API will, with upcoming patches:
- parse and compose SDP fmtp strings
- manage SDP codecs and codec lists (like 'AMR:octet-align=1#112')
- parse and compose entire SDP messages

In this patch: parse and compose SDP fmtp strings.

Add libosmo-sdp build fu.
Add sdp/fmtp.h and sdp/sdp_strings.h.
Add tests/sdp/sdp_fmtp_test.c.

Change-Id: I6128852f4d249e90319f43d6cd6ed0a9a2ed0430
2025-01-14 14:01:31 +01:00
Neels Hofmeyr
7338fd833b make libosmo-mgcp.a an .la lib
.la libs aka LTLIB can include library dependencies.

Rationale: An upcoming patch adds noinst libosmo-sdp, used by both
osmo-mgw and libosmo-mgcp-client, and the cleanest way to link that is
via a library dependency. Prepare for that.

Change-Id: I5302d56e571d0da9061c275ce13d4f085c044671
2025-01-14 14:01:31 +01:00
38 changed files with 5454 additions and 11 deletions

View File

@@ -18,6 +18,7 @@ SUBDIRS = \
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = \
libosmo-sdp.pc \
libosmo-mgcp-client.pc \
$(NULL)

View File

@@ -187,11 +187,14 @@ AM_CONFIG_HEADER(bscconfig.h)
AC_OUTPUT(
libosmo-mgcp-client.pc
libosmo-sdp.pc
include/Makefile
include/osmocom/Makefile
include/osmocom/sdp/Makefile
include/osmocom/mgcp_client/Makefile
include/osmocom/mgcp/Makefile
src/Makefile
src/libosmo-sdp/Makefile
src/libosmo-mgcp-client/Makefile
src/libosmo-mgcp/Makefile
src/osmo-mgw/Makefile
@@ -199,6 +202,7 @@ AC_OUTPUT(
tests/atlocal
tests/mgcp_client/Makefile
tests/mgcp/Makefile
tests/sdp/Makefile
doc/Makefile
doc/examples/Makefile
doc/manuals/Makefile

15
debian/control vendored
View File

@@ -36,6 +36,21 @@ Multi-Arch: same
Depends: libosmo-mgcp-client14 (= ${binary:Version}), ${misc:Depends}
Description: libosmo-mgcp-client: Osmocom's Media Gateway Control Protocol client utilities
Package: libosmo-sdp1
Section: libs
Architecture: any
Multi-Arch: same
Pre-Depends: ${misc:Pre-Depends}
Depends: ${misc:Depends}, ${shlibs:Depends}
Description: libosmo-sdp: Osmocom's Session Description Protocol encoder and decoder
Package: libosmo-sdp-dev
Section: libdevel
Architecture: any
Multi-Arch: same
Depends: libosmo-sdp1 (= ${binary:Version}), ${misc:Depends}
Description: libosmo-sdp: Osmocom's Session Description Protocol encoder and decoder
Package: osmo-mgw-doc
Architecture: all
Section: doc

19
debian/copyright vendored
View File

@@ -63,3 +63,22 @@ License: GPL-3.0+
Files: osmoappdesc.py
Copyright: 2013 Katerina Barone-Adesi <kat.obsc@gmail.com>
License: GPL-3.0+
Files: src/libosmo-sdp/* include/osmocom/sdp/*
Copyright: 2024 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
License: GPL-3.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 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 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/>.
.
On Debian systems, the complete text of the GNU General Public License
Version 3 can be found in `/usr/share/common-licenses/GPL-3'.

4
debian/libosmo-sdp-dev.install vendored Normal file
View File

@@ -0,0 +1,4 @@
usr/include/osmocom/sdp
usr/lib/*/libosmo-sdp.so
usr/lib/*/libosmo-sdp.a
usr/lib/*/pkgconfig/libosmo-sdp.pc

1
debian/libosmo-sdp1.install vendored Normal file
View File

@@ -0,0 +1 @@
usr/lib/*/libosmo-sdp.so.*

View File

@@ -8,6 +8,11 @@ nobase_include_HEADERS = \
osmocom/mgcp_client/mgcp_client_endpoint_fsm.h \
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_codec_list.h \
osmocom/sdp/sdp_msg.h \
osmocom/sdp/sdp_strings.h \
$(NULL)
noinst_HEADERS = \

View File

@@ -1,4 +1,5 @@
SUBDIRS = \
sdp \
mgcp_client \
mgcp \
$(NULL)

View File

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

View File

@@ -0,0 +1,34 @@
/* Public API for codec management in SDP messages: managing SDP fmtp strings. */
/*
* (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 <stddef.h>
#include <stdbool.h>
#include <stdint.h>
bool osmo_sdp_fmtp_get_val(char *val, size_t val_size, const char *fmtp, const char *option_name);
int64_t osmo_sdp_fmtp_get_int(const char *fmtp, const char *option_name, int64_t default_value);
bool osmo_sdp_fmtp_amr_is_octet_aligned(const char *fmtp);
bool osmo_sdp_fmtp_amr_match(const char *a, const char *b);

View File

@@ -0,0 +1,126 @@
/* 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;
/* Entry used by osmo_sdp_codec_list. */
struct llist_head entry;
/* 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,69 @@
/* Public API for codec management in SDP messages: list of struct osmo_sdp_codec. */
/*
* (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/sdp/sdp_codec.h>
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);
int osmo_sdp_codec_list_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_codec_list *codec_list, bool summarize);
char *osmo_sdp_codec_list_to_str_c(void *ctx, const struct osmo_sdp_codec_list *codec_list, bool summarize);
struct osmo_sdp_codec *osmo_sdp_codec_list_first(const struct osmo_sdp_codec_list *list);
int osmo_sdp_codec_list_move_to_first(struct osmo_sdp_codec_list *codec_list, const struct osmo_sdp_codec *codec,
const struct osmo_sdp_codec_cmp_flags *cmpf);
#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)
int osmo_sdp_codec_list_cmp(const struct osmo_sdp_codec_list *a, const struct osmo_sdp_codec_list *b,
const struct osmo_sdp_codec_cmp_flags *cmpf);
void osmo_sdp_codec_list_intersection(struct osmo_sdp_codec_list *dst, const struct osmo_sdp_codec_list *other,
const struct osmo_sdp_codec_cmp_flags *cmpf,
bool translate_payload_type_numbers);
struct osmo_sdp_codec *osmo_sdp_codec_list_by_payload_type(struct osmo_sdp_codec_list *codec_list, int8_t payload_type);
bool osmo_sdp_codec_list_is_empty(const struct osmo_sdp_codec_list *codec_list);

View File

@@ -0,0 +1,18 @@
/* 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);
const char *token_chr(const struct token *src, char c);
const char *token_chrs(const struct token *src, const char *chrs);
void token_next(struct token *t, const char *str, const char *end, const char *separators);
const char *token_to_int64(int64_t *result, const struct token *t, int base, int min_val, int max_val);
const char *token_to_int(int *result, const struct token *t, int base, int min_val, int max_val);

View File

@@ -0,0 +1,105 @@
/* Public API for SDP message encoding and decoding */
/*
* (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/sockaddr_str.h>
#include <osmocom/sdp/sdp_codec.h>
#include <osmocom/sdp/sdp_codec_list.h>
/* Media Direction Attributes "a=recvonly", "a=sendrecv", "a=sendonly", "a=inactive" RFC-8866 6.7. */
enum osmo_sdp_media_direcion_e {
OSMO_SDP_MDIR_UNSET = 0,
OSMO_SDP_MDIR_RECVONLY = 1,
OSMO_SDP_MDIR_SENDRECV = 2,
OSMO_SDP_MDIR_SENDONLY = 3,
OSMO_SDP_MDIR_INACTIVE = 4,
};
/* Session Description Protocol (SDP) message, RFC-8866. */
struct osmo_sdp_msg {
/* 5.2 Origin ("o="). */
struct {
struct osmo_sockaddr_str addr;
char *username;
char *sess_id;
char *sess_version;
} origin;
/* 5.3 Session Name ("s="). */
char *session_name;
/* 5.7 Connection Information ("c=") and port from 5.14 Media Descriptions ("m="). */
struct osmo_sockaddr_str rtp;
/* 5.9. Time Active ("t="). */
struct {
int64_t start;
int64_t stop;
} time_active;
/* 6.4 "a=ptime:<val>". */
unsigned int ptime;
/* 6.7 "a=sendrecv"... */
enum osmo_sdp_media_direcion_e media_direction;
/* List of codecs defined in the SDP message.
* This should not be NULL -- osmo_sdp_msg_alloc() returns an empty osmo_sdp_codec_list instance, ready for
* adding codecs.
* Combination of:
* - payload_type numbers from 5.14 Media Descriptions ("m="),
* - 6.6 "a=rtpmap",
* - 6.15 Format Parameters "a=fmtp".
*/
struct osmo_sdp_codec_list *codecs;
/* For future extension, always set to false. */
bool v2;
};
struct osmo_sdp_msg_decode_ret {
int rc;
/* If rc != 0 */
struct {
/* Point at the position that caused the error, in the src string. */
const char *at_input_str;
/* Nr of characters at *src_str that are relevant to the error. */
size_t at_input_str_len;
} error;
/* Pointer to the remaining part of src after parsing one SDP message.
* For example, in MGCP, there may be multiple SDP messages concatenated. */
const char *src_remain;
};
struct osmo_sdp_msg *osmo_sdp_msg_alloc(void *ctx);
struct osmo_sdp_msg *osmo_sdp_msg_decode(void *ctx, const char *src_str, int src_str_len,
struct osmo_sdp_msg_decode_ret *ret);
int osmo_sdp_msg_encode_buf(char *dst, size_t dst_size, const struct osmo_sdp_msg *sdp);
char *osmo_sdp_msg_encode_c(void *ctx, const struct osmo_sdp_msg *sdp);
int osmo_sdp_msg_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_msg *sdp, bool summarize);
char *osmo_sdp_msg_to_str_c(void *ctx, const struct osmo_sdp_msg *sdp, bool summarize);

View File

@@ -0,0 +1,39 @@
/* Central definition of string tokens used for parsing and composing SDP messages */
#pragma once
#define OSMO_SDP_STR_MEDIA "m"
#define OSMO_SDP_STR_ATTRIB "a"
#define OSMO_SDP_STR_TIME_ACTIVE "t"
#define OSMO_SDP_STR_RTPMAP "rtpmap"
#define OSMO_SDP_STR_FMTP "fmtp"
#define OSMO_SDP_STR_PTIME "ptime"
/*! "a=foo:" */
#define OSMO_SDP_A_PREFIX(STR) OSMO_SDP_STR_ATTRIB "=" STR ":"
/*! "a=fmtp:" */
#define OSMO_SDP_STR_A_FMTP OSMO_SDP_A_PREFIX(OSMO_SDP_STR_FMTP)
/* Media Direction Attributes "a=recvonly", "a=sendrecv", "a=sendonly", "a=inactive" RFC-8866 6.7. */
#define OSMO_SDP_STR_RECVONLY "recvonly"
#define OSMO_SDP_STR_SENDRECV "sendrecv"
#define OSMO_SDP_STR_SENDONLY "sendonly"
#define OSMO_SDP_STR_INACTIVE "inactive"
/* AMR related tokens */
#define OSMO_SDP_STR_AMR_OCTET_ALIGN "octet-align"
/*! "octet-align=1" */
#define OSMO_SDP_STR_AMR_OCTET_ALIGN_1 OSMO_SDP_STR_AMR_OCTET_ALIGN "=1"
/*! "octet-align=0".
* According to spec [1], "octet-align=0" is identical to omitting 'octet-align' entirely. In Osmocom practice, whether
* or not "octet-align=0" is present can make a big difference for osmo-mgw versions 1.12 and older, which do not heed
* [1].
*
* spec [1]: RFC4867, see details in description of osmo_sdp_fmtp_amr_is_octet_aligned().
*/
#define OSMO_SDP_STR_AMR_OCTET_ALIGN_0 OSMO_SDP_STR_AMR_OCTET_ALIGN "=0"

10
libosmo-sdp.pc.in Normal file
View File

@@ -0,0 +1,10 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: Osmocom Session Description Protocol coding library
Description: C Utility Library
Version: @VERSION@
Libs: -L${libdir} -losmo-sdp
Cflags: -I${includedir}/

View File

@@ -15,6 +15,7 @@ AM_CFLAGS = \
# Libraries
SUBDIRS = \
libosmo-sdp \
libosmo-mgcp-client \
libosmo-mgcp \
$(NULL)

View File

@@ -19,15 +19,15 @@ AM_LDFLAGS = \
$(COVERAGE_LDFLAGS) \
$(NULL)
noinst_LIBRARIES = \
libosmo-mgcp.a \
noinst_LTLIBRARIES = \
libosmo-mgcp.la \
$(NULL)
noinst_HEADERS = \
g711common.h \
$(NULL)
libosmo_mgcp_a_SOURCES = \
libosmo_mgcp_la_SOURCES = \
mgcp_protocol.c \
mgcp_network.c \
mgcp_vty.c \
@@ -43,3 +43,12 @@ libosmo_mgcp_a_SOURCES = \
mgcp_e1.c \
mgcp_iuup.c \
$(NULL)
libosmo_mgcp_la_LIBADD = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMONETIF_LIBS) \
$(LIBOSMOABIS_LIBS) \
$(LIBOSMOTRAU_LIBS) \
$(NULL)

View File

@@ -0,0 +1,38 @@
AM_CPPFLAGS = \
$(all_includes) \
-I$(top_srcdir)/include \
-I$(top_builddir) \
$(NULL)
AM_CFLAGS = \
-Wall \
$(LIBOSMOCORE_CFLAGS) \
$(TALLOC_CFLAGS) \
$(NULL)
SDP_LIBVERSION=1:0:0
# This is not at all related to the release version, but a range of supported
# API versions. Read TODO_RELEASE in the source tree's root!
lib_LTLIBRARIES = \
libosmo-sdp.la \
$(NULL)
libosmo_sdp_la_SOURCES = \
sdp_codec.c \
sdp_codec_list.c \
sdp_msg.c \
sdp_internal.c \
fmtp.c \
$(NULL)
libosmo_sdp_la_LDFLAGS = \
$(AM_LDFLAGS) \
-version-info $(SDP_LIBVERSION) \
-no-undefined \
-export-symbols-regex '^osmo_' \
$(NULL)
libosmo_sdp_la_LIBADD = \
$(LIBOSMOCORE_LIBS) \
$(NULL)

193
src/libosmo-sdp/fmtp.c Normal file
View File

@@ -0,0 +1,193 @@
/*
* (C) 2023-2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include <string.h>
#include <ctype.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/logging.h>
#include <osmocom/sdp/sdp_strings.h>
#include <osmocom/sdp/fmtp.h>
/* End of current fmtp parameter. Return a pointer to the next ';' character, if present, or the terminating '\0'. */
static const char *osmo_sdp_fmtp_end(const char *fmtp)
{
if (!fmtp)
return NULL;
for (; *fmtp && *fmtp != ';'; fmtp++);
return fmtp;
}
/* Start of next fmtp parameter. Return a pointer to the first character of the next fmtp parameter's name, or the
* terminating '\0'. */
static const char *osmo_sdp_fmtp_next(const char *fmtp)
{
if (!fmtp)
return NULL;
fmtp = osmo_sdp_fmtp_end(fmtp);
for (; *fmtp && (*fmtp == ';' || isspace(*fmtp)); fmtp++);
return fmtp;
}
/*! Parse a given SDP fmtp value string, returning the value of a specific option, if present.
*
* Example:
*
* const char *fmtp_vals = "octet-align=1;mode-set=0,2,4,7";
*
* char mode_set_str[23];
* if (osmo_sdp_fmtp_get_val(mode_set_str, sizeof(mode_set_str), fmtp_vals, "mode-set")) {
* // option 'mode-set' is present, now mode_set_str == "0,2,4,7"
* use_modeset(mode_set_str);
* } else {
* // if 'mode-set' were not present...
* use_modeset(MY_DEFAULT_MODESET);
* }
*
* \param[out] val Buffer to write the option's value to.
* \param[in] val_size Space available in val.
* \param[in] fmtp fmtp value string to parse -- must not contain the "a=fmtp:N " prefix, only the value part.
* \param[in] option_name Which fmtp option to get the value for.
* \return true when the option was found, false when it was not present.
*/
bool osmo_sdp_fmtp_get_val(char *val, size_t val_size, const char *fmtp, const char *option_name)
{
const char *pos = fmtp;
const char *end;
int option_name_len = strlen(option_name);
for (; pos && *pos; pos = osmo_sdp_fmtp_next(pos)) {
if (!osmo_str_startswith(pos, option_name))
continue;
pos += option_name_len;
if (*pos != '=')
continue;
pos++;
break;
}
if (!pos || !*pos)
return false;
end = osmo_sdp_fmtp_end(pos);
OSMO_ASSERT(end);
if (val && val_size)
osmo_strlcpy(val, pos, OSMO_MIN(val_size, end - pos + 1));
return true;
}
/*! Parse a given SDP fmtp value string, returning the value of a specific integer option, if present.
*
* Example:
*
* const char *fmtp_vals = "octet-align=1;mode-set=0,2,4,7";
* bool oa = osmo_sdp_fmtp_get_int(fmtp_vals, OSMO_SDP_AMR_OCTET_ALIGN_NAME, 1);
*
* \param[in] fmtp fmtp value string to parse -- must not contain the "a=fmtp:N " prefix, only the value part.
* \param[in] option_name Which fmtp option to get the value for.
* \param[in] default_value If option_name is not present or cannot be parsed as integer, return this instead.
* \return the integer value when the option was found and actually an integer, default_value otherwise.
*/
int64_t osmo_sdp_fmtp_get_int(const char *fmtp, const char *option_name, int64_t default_value)
{
char val[128];
if (!osmo_sdp_fmtp_get_val(val, sizeof(val), fmtp, option_name))
return default_value;
if (!val[0])
return default_value;
int64_t i;
if (osmo_str_to_int64(&i, val, 10, INT64_MIN, INT64_MAX)) {
/* error parsing number */
return default_value;
}
return i;
}
/*! Return true if octet-align is present and set to 1 in the given AMR related fmtp value.
* Default to octet-align=0, i.e. bandwidth-efficient mode.
*
* See RFC4867 "RTP Payload Format for AMR and AMR-WB" sections "8.1. AMR Media Type Registration" and "8.2. AMR-WB
* Media Type Registration":
*
* octet-align: Permissible values are 0 and 1. If 1, octet-align
* operation SHALL be used. If 0 or if not present,
* bandwidth-efficient operation is employed.
*
* https://tools.ietf.org/html/rfc4867
*/
bool osmo_sdp_fmtp_amr_is_octet_aligned(const char *fmtp)
{
return osmo_sdp_fmtp_get_int(fmtp, OSMO_SDP_STR_AMR_OCTET_ALIGN, 0) == 1;
}
static void strip_whitespace(char *str)
{
char *i = str;
char *o = str;
for (; *i; i++, o++) {
while (isspace(*i))
i++;
*o = *i;
if (!*i)
break;
}
}
/* Return true when the two AMR type fmtp strings can be considered equivalent.
* - Omission of octet-align is equivalent to having octet-align=0 present (0 is the default).
* - Omission of 'mode-set' means, match any and all codec modes. So if either a or b have no 'mode-set', it's a match.
* If both have 'mode-set' present, they must be identical to match. Do not sort the mode-set string, but strip
* whitespace.
* - TODO all other parameters are currently completely ignored.
*/
bool osmo_sdp_fmtp_amr_match(const char *a, const char *b)
{
char a_modeset[32] = {};
char b_modeset[32] = {};
bool a_ok;
bool b_ok;
if (!a)
a = "";
if (!b)
b = "";
/* octet-align=1. Omission means octet-align=0 */
if (osmo_sdp_fmtp_amr_is_octet_aligned(a) != osmo_sdp_fmtp_amr_is_octet_aligned(b))
return false;
/* mode-set=0,1,2,3,4,5,6,7 */
a_ok = osmo_sdp_fmtp_get_val(a_modeset, sizeof(a_modeset), a, "mode-set");
b_ok = osmo_sdp_fmtp_get_val(b_modeset, sizeof(b_modeset), b, "mode-set");
if (a_ok && b_ok) {
/* Strip whitespace: We don't know what remote SDP peers may throw at us. There could be whitespace
* around the separators like 'mode-set=2,3 ; octet-align=1', which may show up here as whitespace in
* the value string as "2,3 ", which would mismatch "2,3". */
strip_whitespace(a_modeset);
strip_whitespace(b_modeset);
if (strcmp(a_modeset, b_modeset))
return false;
}
/* TODO: treat other AMR traits, see RFC4867 8.1. Maybe generically match all values that are present?
* So far we have no need for other values than octet-align and mode-set. */
/* No mismatch found, it's a match */
return true;
}

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,367 @@
/* 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 <string.h>
#include <osmocom/core/utils.h>
#include <osmocom/sdp/sdp_codec_list.h>
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 = osmo_sdp_codec_list_first(codec_list))) {
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);
}
static inline int strcmp_safe(const char *a, const char *b)
{
return strcmp(a ? : "", b ? : "");
}
/*! Short single-line representation of a list of SDP audio codecs, convenient for logging.
* If summarize == true, collapse variants of the same encoding_name (in practice, don't show all of the various AMR
* fmtp permutations). If summarize == false, print each and every codec in full.
*/
int osmo_sdp_codec_list_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_codec_list *codec_list, bool summarize)
{
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
const struct osmo_sdp_codec *codec;
bool first;
if (llist_empty(&codec_list->list)) {
OSMO_STRBUF_PRINTF(sb, "(no-codecs)");
return sb.chars_needed;
}
if (!summarize) {
first = true;
osmo_sdp_codec_list_foreach (codec, codec_list) {
if (!first)
OSMO_STRBUF_PRINTF(sb, " ");
OSMO_STRBUF_APPEND(sb, osmo_sdp_codec_to_str_buf, codec);
first = false;
}
return sb.chars_needed;
}
/* summarize */
first = true;
osmo_sdp_codec_list_foreach (codec, codec_list) {
const struct osmo_sdp_codec *c2;
int count = 0;
bool various_pt = false;
/* When this encoding name has been handled before, skip it now. */
osmo_sdp_codec_list_foreach (c2, codec_list) {
if (c2 == codec)
break;
if (!strcmp_safe(codec->encoding_name, c2->encoding_name)) {
count = 1;
break;
}
}
if (count)
continue;
/* Not seen this encoding_name before, count total occurences */
count = 0;
osmo_sdp_codec_list_foreach (c2, codec_list) {
if (!strcmp_safe(codec->encoding_name, c2->encoding_name)) {
count++;
if (codec->payload_type != c2->payload_type)
various_pt = true;
}
}
if (!first)
OSMO_STRBUF_PRINTF(sb, " ");
if (count > 1)
OSMO_STRBUF_PRINTF(sb, "%d*", count);
OSMO_STRBUF_PRINTF(sb, "%s", codec->encoding_name);
if (!various_pt)
OSMO_STRBUF_PRINTF(sb, "#%d", codec->payload_type);
first = false;
}
return sb.chars_needed;
}
char *osmo_sdp_codec_list_to_str_c(void *ctx, const struct osmo_sdp_codec_list *codec_list, bool summarize)
{
OSMO_NAME_C_IMPL(ctx, 128, "osmo_sdp_codec_list_to_str_c-ERROR", osmo_sdp_codec_list_to_str_buf, codec_list, summarize)
}
/*! Return first entry, or NULL if the list is empty. */
struct osmo_sdp_codec *osmo_sdp_codec_list_first(const struct osmo_sdp_codec_list *list)
{
return llist_first_entry_or_null(&list->list, struct osmo_sdp_codec, entry);
}
/*! Move entries matching 'codec' to the front of the list. Matching is done via osmo_sdp_codec_cmp(cmpf).
* Return the number of matches that are now at the front of the list.
*/
int osmo_sdp_codec_list_move_to_first(struct osmo_sdp_codec_list *codec_list, const struct osmo_sdp_codec *codec,
const struct osmo_sdp_codec_cmp_flags *cmpf)
{
struct llist_head *head = &codec_list->list;
struct osmo_sdp_codec *i, *j;
int matches_found = 0;
osmo_sdp_codec_list_foreach_safe (i, j, codec_list) {
if (osmo_sdp_codec_cmp(codec, i, cmpf))
continue;
/* It's a match, move to the head */
osmo_sdp_codec_list_remove_entry(i);
llist_add(&i->entry, head);
matches_found++;
/* If more matches show up later, add them *after* the one just moved to the front. */
head = &i->entry;
}
return matches_found;
}
/*! Compare two lists of SDP codecs, returning cmp result: -1 if a < b, 0 if a == b, 1 if a > b.
* The two lists are compared in order, item by item, using osmo_sdp_codec_cmp(cmpf).
*/
int osmo_sdp_codec_list_cmp(const struct osmo_sdp_codec_list *a, const struct osmo_sdp_codec_list *b,
const struct osmo_sdp_codec_cmp_flags *cmpf)
{
const struct llist_head *a_start;
const struct llist_head *a_pos;
const struct llist_head *b_start;
const struct llist_head *b_pos;
int cmp;
/* NULL pointer == empty list */
if (a && llist_empty(&a->list))
a = NULL;
if (b && llist_empty(&a->list))
b = NULL;
/* are one or both empty? */
if (a == b)
return 0;
if (!a)
return -1;
if (!b)
return 1;
/* compare item by item */
a_start = &a->list;
a_pos = a_start->next;
b_start = &b->list;
b_pos = b_start->next;
for (; a_pos != a_start; a_pos = a_pos->next, b_pos = b_pos->next) {
const struct osmo_sdp_codec *codec_a;
const struct osmo_sdp_codec *codec_b;
if (b_pos == b_start) {
/* there is an entry in a, but b has already ended. mismatch. */
return 1;
}
codec_a = llist_entry(a_pos, struct osmo_sdp_codec, entry);
codec_b = llist_entry(b_pos, struct osmo_sdp_codec, entry);
cmp = osmo_sdp_codec_cmp(codec_a, codec_b, cmpf);
if (cmp)
return cmp;
}
if (b_pos != b_start) {
/* 'a' has ended, but 'b' has more items. mismatch. */
return -1;
}
/* full match. */
return 0;
}
/*! Leave only those codecs in 'dst' that are also present in 'other'.
* The matching is made by osmo_sdp_codec_cmp(cmpf).
* If translate_payload_type_numbers has an effect if 'dst' and '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 'other'.
* If false, keep payload_type numbers in 'dst' unchanged. */
void osmo_sdp_codec_list_intersection(struct osmo_sdp_codec_list *dst, const struct osmo_sdp_codec_list *other,
const struct osmo_sdp_codec_cmp_flags *cmpf,
bool translate_payload_type_numbers)
{
struct osmo_sdp_codec *i, *j;
osmo_sdp_codec_list_foreach_safe (i, j, dst) {
struct osmo_sdp_codec *o;
struct osmo_sdp_codec *match = NULL;
osmo_sdp_codec_list_foreach (o, other) {
if (osmo_sdp_codec_cmp(i, o, cmpf))
continue;
match = o;
break;
}
if (!match) {
osmo_sdp_codec_list_remove_entry(i);
talloc_free(i);
continue;
}
if (translate_payload_type_numbers)
i->payload_type = match->payload_type;
}
}
/* Find an entry for the given payload_type number in the given list of codecs. */
struct osmo_sdp_codec *osmo_sdp_codec_list_by_payload_type(struct osmo_sdp_codec_list *codec_list, int8_t payload_type)
{
struct osmo_sdp_codec *codec;
osmo_sdp_codec_list_foreach(codec, codec_list) {
if (codec->payload_type == payload_type)
return codec;
}
return NULL;
}
bool osmo_sdp_codec_list_is_empty(const struct osmo_sdp_codec_list *codec_list)
{
if (!codec_list)
return true;
return llist_empty(&codec_list->list);
}

View File

@@ -0,0 +1,121 @@
/*
* (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 <ctype.h>
#include <limits.h>
#include <errno.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);
}
const char *token_chr(const struct token *src, char c)
{
const char *pos;
for (pos = src->start; *pos && pos < src->end; pos++) {
if (*pos == c)
return pos;
}
return NULL;
}
const char *token_chrs(const struct token *src, const char *chrs)
{
const char *pos;
for (pos = src->start; pos && *pos && pos < src->end; pos++) {
if (strchr(chrs, *pos))
return pos;
}
return NULL;
}
void token_next(struct token *t, const char *str, const char *end, const char *separators)
{
t->start = str;
while (strchr(separators, *t->start) && t->start < end)
t->start++;
t->end = t->start;
while (!strchr(separators, *t->end) && t->end < end)
t->end++;
}
/* Convert the token to an integer, and return the character afer the integer string that was parsed.
* The difference to osmo_str_to_int64() is that this guarantees to only access the memory from t->start to t->end. */
const char *token_to_int64(int64_t *result, const struct token *t, int base, int min_val, int max_val)
{
/* copy the number section over to a temporary buffer, because t->end may terminate the string anywhere, and
* functions like strtoll (which osmo_str_to_int64() uses) are not able to stay within a strict buffer length
* unless the string is zero terminated at the buffer boundary. */
char buf[32];
const char *int_end = t->start;
if (int_end < t->end && *int_end == '-')
int_end++;
while (int_end < t->end && isdigit(*int_end))
int_end++;
if (int_end - t->start >= sizeof(buf))
return NULL;
osmo_strlcpy(buf, t->start, int_end - t->start + 1);
if (osmo_str_to_int64(result, buf, base, min_val, max_val) != 0)
return NULL;
return int_end;
}
/* Convenience: like token_to_int64() but with a plain int. */
const char *token_to_int(int *result, const struct token *t, int base, int min_val, int max_val)
{
int64_t val;
const char *rc = token_to_int64(&val, t, base, min_val, max_val);
if (val < INT_MIN) {
if (result)
*result = INT_MIN;
return NULL;
}
if (val > INT_MAX) {
if (result)
*result = INT_MAX;
return NULL;
}
if (result)
*result = (int)val;
return rc;
}

517
src/libosmo-sdp/sdp_msg.c Normal file
View File

@@ -0,0 +1,517 @@
/* Implementation for SDP message encoding and decoding */
/*
* (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 <inttypes.h>
#include <errno.h>
#include <ctype.h>
#include <limits.h>
#include <osmocom/core/utils.h>
#include <osmocom/sdp/sdp_msg.h>
#include <osmocom/sdp/sdp_strings.h>
#include <osmocom/sdp/sdp_internal.h>
static const char * const mdir_str[] = {
[OSMO_SDP_MDIR_UNSET] = "-",
[OSMO_SDP_MDIR_SENDONLY] = OSMO_SDP_STR_SENDONLY,
[OSMO_SDP_MDIR_RECVONLY] = OSMO_SDP_STR_RECVONLY,
[OSMO_SDP_MDIR_SENDRECV] = OSMO_SDP_STR_SENDRECV,
[OSMO_SDP_MDIR_INACTIVE] = OSMO_SDP_STR_INACTIVE,
};
/*! Convert struct osmo_sdp_msg to the actual SDP protocol representation. */
int osmo_sdp_msg_encode_buf(char *dst, size_t dst_size, const struct osmo_sdp_msg *sdp)
{
const struct osmo_sdp_codec *codec;
struct osmo_strbuf sb = { .buf = dst, .len = dst_size };
const char *oip;
char oipv;
const char *ip;
char ipv;
if (!sdp) {
OSMO_STRBUF_PRINTF(sb, "%s", "");
return sb.chars_needed;
}
oip = sdp->origin.addr.ip[0] ? sdp->origin.addr.ip : "0.0.0.0";
oipv = (osmo_ip_str_type(oip) == AF_INET6) ? '6' : '4';
ip = sdp->rtp.ip[0] ? sdp->rtp.ip : "0.0.0.0";
ipv = (osmo_ip_str_type(oip) == AF_INET6) ? '6' : '4';
OSMO_STRBUF_PRINTF(sb,
"v=0\r\n"
"o=%s %s %s IN IP%c %s\r\n"
"s=%s\r\n"
"c=IN IP%c %s\r\n"
"t=%"PRId64" %"PRId64"\r\n"
"m=audio %d RTP/AVP",
sdp->origin.username ? : "libosmo-sdp",
sdp->origin.sess_id ? : "0", sdp->origin.sess_version ? : "0",
oipv, oip,
sdp->session_name ? : "-",
ipv, ip,
sdp->time_active.start,
sdp->time_active.stop,
sdp->rtp.port);
/* Append all payload type numbers to 'm=audio <port> RTP/AVP 3 4 112' line */
osmo_sdp_codec_list_foreach(codec, sdp->codecs)
OSMO_STRBUF_PRINTF(sb, " %d", codec->payload_type);
OSMO_STRBUF_PRINTF(sb, "\r\n");
/* Add details for all codecs */
osmo_sdp_codec_list_foreach(codec, sdp->codecs) {
if (!osmo_sdp_codec_is_set(codec))
continue;
OSMO_STRBUF_PRINTF(sb, OSMO_SDP_A_PREFIX(OSMO_SDP_STR_RTPMAP) "%d %s/%d\r\n", codec->payload_type, codec->encoding_name,
codec->rate > 0 ? codec->rate : 8000);
if (codec->fmtp && codec->fmtp[0])
OSMO_STRBUF_PRINTF(sb, OSMO_SDP_A_PREFIX(OSMO_SDP_STR_FMTP) "%d %s\r\n", codec->payload_type, codec->fmtp);
}
if (sdp->ptime)
OSMO_STRBUF_PRINTF(sb, OSMO_SDP_A_PREFIX(OSMO_SDP_STR_PTIME) "%d\r\n", sdp->ptime);
if (sdp->media_direction != OSMO_SDP_MDIR_UNSET && sdp->media_direction < ARRAY_SIZE(mdir_str))
OSMO_STRBUF_PRINTF(sb, "a=%s\r\n", mdir_str[sdp->media_direction]);
return sb.chars_needed;
}
char *osmo_sdp_msg_encode_c(void *ctx, const struct osmo_sdp_msg *sdp)
{
OSMO_NAME_C_IMPL(ctx, 256, "osmo_sdp_msg_to_str_c-ERROR", osmo_sdp_msg_encode_buf, sdp)
}
/* Return the first line ending (or the end of the string) at or after the given string position. */
static const char *get_line_end(const struct token *src)
{
const char *line_end = token_chrs(src, "\r\n");
if (!line_end)
return src->end;
return line_end;
}
/* See if str starts with an attribute like "rtpmap:" or "sendrecv\r\n".
* For example:
* token_is_attrib({"foo: bar"}, "foo", ':') --> " bar"
* token_is_attrib({"sendrev\n"}, "sendrecv", 0) --> "\n"
* token_is_attrib({"sendrev\n"}, "foo", 0) --> NULL
* On mismatch, return NULL.
* On match, return the string after the expect_next_char.
* For expect_next_char == 0, match both the end of the string as well as a line ending character ('\r' or '\n'), and in
* both cases return the string directly after attrib_name.
*/
static const char *token_is_attrib(const struct token *str, const char *attrib_name, char expect_next_char)
{
const char *next_c;
int attrib_name_len = strlen(attrib_name);
if (str->start + attrib_name_len > str->end)
return NULL;
if (!osmo_str_startswith(str->start, attrib_name))
return NULL;
next_c = str->start + strlen(attrib_name);
if (next_c > str->end)
return NULL;
if (next_c == str->end) {
if (!expect_next_char)
return str->end;
return NULL;
}
if (expect_next_char == *next_c)
return next_c + 1;
/* Treat expect_next_char == \0 as equivalent with line end */
if (!expect_next_char && strchr("\r\n", *next_c))
return next_c;
return NULL;
}
static enum osmo_sdp_media_direcion_e check_for_media_direction(const struct token *str)
{
int i;
for (i = 0; i < ARRAY_SIZE(mdir_str); i++) {
if (i == OSMO_SDP_MDIR_UNSET)
continue;
if (token_is_attrib(str, mdir_str[i], 0))
return i;
}
return OSMO_SDP_MDIR_UNSET;
}
static struct osmo_sdp_codec *find_or_create_payload_type(struct osmo_sdp_msg *sdp, unsigned int payload_type)
{
struct osmo_sdp_codec *codec;
codec = osmo_sdp_codec_list_by_payload_type(sdp->codecs, payload_type);
if (!codec) {
codec = osmo_sdp_codec_list_add_empty(sdp->codecs);
codec->payload_type = payload_type;
codec->rate = 8000;
}
return codec;
}
static void next_token(struct token *t, const char *str, const char *end)
{
token_next(t, str, end, " \t");
}
/* 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'
*/
static int sdp_parse_attrib(struct osmo_sdp_msg *sdp, const struct token *src)
{
unsigned int payload_type;
struct osmo_sdp_codec *codec;
enum osmo_sdp_media_direcion_e mdir;
const char *line_end = get_line_end(src);
const char *next;
int nr;
struct token t;
if ((next = token_is_attrib(src, OSMO_SDP_STR_RTPMAP, ':'))) {
/* "a=rtpmap:96 AMR/8000" */
next_token(&t, next, line_end);
next = token_to_int(&nr, &t, 10, 0, 127);
if (next != t.end)
return -EINVAL;
payload_type = nr;
token_next(&t, next, line_end, " \t/");
if (t.start >= t.end)
return -EINVAL;
next = t.end;
codec = find_or_create_payload_type(sdp, payload_type);
token_copy(codec, &codec->encoding_name, &t);
token_next(&t, next, line_end, " \t/");
if (t.start >= t.end) {
/* There should be a "/8000" here. If it is missing, let's not be strict about it. */
codec->rate = 8000;
} else {
next = token_to_int(&nr, &t, 10, 0, INT_MAX);
if (next != t.end)
return -EINVAL;
}
/* optional channel number, i.e. another "/1" */
token_next(&t, t.end, line_end, " \t/");
if (t.end > t.start) {
int channels;
next = token_to_int(&channels, &t, 10, 0, INT_MAX);
if (next != t.end)
return -EINVAL;
if (channels != 1)
return -ENOTSUP;
}
}
else if ((next = token_is_attrib(src, OSMO_SDP_STR_FMTP, ':'))) {
/* "a=fmtp:112 octet-align=1;mode-set=0,1,2,3" */
next_token(&t, next, line_end);
next = token_to_int(&nr, &t, 10, 0, 127);
if (next != t.end)
return -EINVAL;
payload_type = nr;
codec = find_or_create_payload_type(sdp, payload_type);
/* "octet-align..." token */
next_token(&t, next, line_end);
if (t.start >= line_end)
return -EINVAL;
t.end = line_end;
token_copy(codec, &codec->fmtp, &t);
}
else if ((next = token_is_attrib(src, OSMO_SDP_STR_PTIME, ':'))) {
/* "a=ptime:20" */
next_token(&t, next, line_end);
next = token_to_int(&nr, &t, 10, 1, INT_MAX);
if (!next)
return -EINVAL;
/* RFC8866 6.4: it could also be a 'real' nr, but we don't support that. */
if (next < t.end)
return -ENOTSUP;
sdp->ptime = nr;
}
/* "a=sendrecv" ... */
else if ((mdir = check_for_media_direction(src)) != OSMO_SDP_MDIR_UNSET) {
sdp->media_direction = mdir;
}
return 0;
}
static const struct value_string fixed_payload_types[] = {
{ 0, "PCMU" },
{ 3, "GSM" },
{ 8, "PCMA" },
{ 18, "G729" },
{ 110, "GSM-EFR" },
{ 111, "GSM-HR-08" },
{ 112, "AMR" },
{ 113, "AMR-WB" },
{}
};
/* Parse a line like 'm=audio 16398 RTP/AVP 0 3 8 96 112', starting after the '=' */
static int sdp_parse_media_description(struct osmo_sdp_msg *sdp, const struct token *src)
{
unsigned int port;
struct token t;
const char *line_end = get_line_end(src);
if (sscanf(src->start, "audio %u RTP/AVP", &port) < 1)
return -ENOTSUP;
if (port > 0xffff)
return -EINVAL;
sdp->rtp.port = port;
/* skip "audio 12345 RTP/AVP ", i.e. 3 tokens on */
next_token(&t, src->start, line_end);
next_token(&t, t.end, line_end);
next_token(&t, t.end, line_end);
/* first payload type number */
next_token(&t, t.end, line_end);
/* Parse listing of payload type numbers after "RTP/AVP" */
while (t.start < line_end) {
unsigned int payload_type;
struct osmo_sdp_codec *codec;
const char *encoding_name;
if (sscanf(t.start, "%u", &payload_type) < 1)
return -EINVAL;
codec = find_or_create_payload_type(sdp, payload_type);
/* Fill in encoding name for fixed payload types */
encoding_name = get_value_string_or_null(fixed_payload_types, codec->payload_type);
if (encoding_name)
osmo_talloc_replace_string(codec, &codec->encoding_name, encoding_name);
next_token(&t, t.end, line_end);
}
return 0;
}
/* parse a line like 'c=IN IP4 192.168.11.151' starting after the '=' */
static int sdp_parse_connection_info(struct osmo_sdp_msg *sdp, const struct token *src)
{
char ipv[10];
char addr_str[INET6_ADDRSTRLEN];
if (sscanf(src->start, "IN %s %s", ipv, addr_str) < 2)
return -EINVAL;
if (strcmp(ipv, "IP4") && strcmp(ipv, "IP6"))
return -ENOTSUP;
return osmo_sockaddr_str_from_str(&sdp->rtp, addr_str, sdp->rtp.port);
}
/* parse a line like 'o=jdoe 3724394400 3724394405 IN IP4 198.51.100.1' starting after the '=' */
static int sdp_parse_origin(struct osmo_sdp_msg *sdp, const struct token *src)
{
struct token t;
char addr_str[INET6_ADDRSTRLEN + 1] = {};
const char *line_end = get_line_end(src);
next_token(&t, src->start, line_end);
token_copy(sdp, &sdp->origin.username, &t);
next_token(&t, t.end, line_end);
token_copy(sdp, &sdp->origin.sess_id, &t);
next_token(&t, t.end, line_end);
token_copy(sdp, &sdp->origin.sess_version, &t);
next_token(&t, t.end, line_end);
if (strncmp("IN", t.start, t.end - t.start))
return -ENOTSUP;
next_token(&t, t.end, line_end);
if (strncmp("IP4", t.start, t.end - t.start)
&& strncmp("IP6", t.start, t.end - t.start))
return -ENOTSUP;
next_token(&t, t.end, line_end);
osmo_strlcpy(addr_str, t.start, OSMO_MIN(sizeof(addr_str), t.end - t.start + 1));
return osmo_sockaddr_str_from_str(&sdp->origin.addr, addr_str, 0);
}
static int sdp_parse_session_name(struct osmo_sdp_msg *sdp, const struct token *src)
{
struct token t = *src;
t.end = get_line_end(src);
if (sdp->session_name)
talloc_free(sdp->session_name);
if (t.start >= t.end)
sdp->session_name = NULL;
else
token_copy(sdp, &sdp->session_name, &t);
return 0;
}
struct osmo_sdp_msg *osmo_sdp_msg_alloc(void *ctx)
{
struct osmo_sdp_msg *sdp;
sdp = talloc_zero(ctx, struct osmo_sdp_msg);
sdp->codecs = osmo_sdp_codec_list_alloc(sdp);
return sdp;
}
/* Parse SDP string into struct osmo_sdp_msg. Return 0 on success, negative on error.
* Return a new osmo_sdp_msg instance allocated from ctx, or NULL on error.
* When NULL is returned and if ret is non-NULL, details of the error are returned in ret->*.
*/
struct osmo_sdp_msg *osmo_sdp_msg_decode(void *ctx, const char *src_str, int src_str_len,
struct osmo_sdp_msg_decode_ret *ret)
{
struct osmo_sdp_msg *sdp;
bool found_message_start = false;
struct token src = {
.start = src_str,
.end = src_str + (src_str_len < 0 ? strlen(src_str) : src_str_len),
};
if (ret)
*ret = (struct osmo_sdp_msg_decode_ret){};
sdp = osmo_sdp_msg_alloc(ctx);
for (; src.start && src.start < src.end && *src.start; src.start++) {
char attrib;
int rc = 0;
struct token t;
if (*src.start == '\r' || *src.start == '\n')
continue;
t.start = src.start;
t.end = get_line_end(&src);
if (!found_message_start) {
/* An SDP message must start with a line saying "v=0". */
if (strncmp("v=0", t.start, t.end - t.start)) {
/* report the error */
if (ret) {
*ret = (struct osmo_sdp_msg_decode_ret){
.rc = -EINVAL,
.error = {
.at_input_str = t.start,
.at_input_str_len = t.end - t.start,
},
.src_remain = t.end,
};
}
talloc_free(sdp);
return NULL;
}
found_message_start = true;
goto next_line;
}
/* Expecting only lines starting with 'X='. Not being too strict about it is probably alright. */
if (t.start + 1 >= t.end || t.start[1] != '=')
goto next_line;
attrib = *t.start;
t.start += 2;
switch (attrib) {
/* a=... */
case 'a':
rc = sdp_parse_attrib(sdp, &t);
break;
case 'm':
rc = sdp_parse_media_description(sdp, &t);
break;
case 'c':
rc = sdp_parse_connection_info(sdp, &t);
break;
case 'o':
rc = sdp_parse_origin(sdp, &t);
break;
case 's':
rc = sdp_parse_session_name(sdp, &t);
break;
default:
/* ignore any other parameters */
break;
}
if (rc) {
if (ret) {
/* shift back to include the 'x=' part as well */
t.start -= 2;
*ret = (struct osmo_sdp_msg_decode_ret){
.rc = rc,
.error = {
.at_input_str = t.start,
.at_input_str_len = t.end - t.start,
},
.src_remain = t.end,
};
}
talloc_free(sdp);
return NULL;
}
next_line:
src.start = t.end;
}
return sdp;
}
/*! Short single-line representation of an SDP message, convenient for logging.
* To obtain a valid SDP message, use osmo_sdp_msg_encode_buf() instead.
*/
int osmo_sdp_msg_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_msg *sdp, bool summarize)
{
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, osmo_sdp_codec_list_to_str_buf, sdp->codecs, summarize);
OSMO_STRBUF_PRINTF(sb, "}");
return sb.chars_needed;
}
char *osmo_sdp_msg_to_str_c(void *ctx, const struct osmo_sdp_msg *sdp, bool summarize)
{
OSMO_NAME_C_IMPL(ctx, 128, "sdp_msg_to_str_c-ERROR", osmo_sdp_msg_to_str_buf, sdp, summarize)
}

View File

@@ -25,12 +25,6 @@ osmo_mgw_SOURCES = \
$(NULL)
osmo_mgw_LDADD = \
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.la \
$(LIBOSMOCTRL_LIBS) \
$(LIBOSMONETIF_LIBS) \
$(LIBOSMOABIS_LIBS) \
$(LIBOSMOTRAU_LIBS) \
$(NULL)

View File

@@ -1,6 +1,7 @@
SUBDIRS = \
mgcp_client \
mgcp \
sdp \
$(NULL)
# The `:;' works around a Bash 3.2 bug when the output is not writeable.

View File

@@ -34,7 +34,7 @@ mgcp_test_SOURCES = \
$(NULL)
mgcp_test_LDADD = \
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.la \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMOGSM_LIBS) \

63
tests/sdp/Makefile.am Normal file
View File

@@ -0,0 +1,63 @@
AM_CPPFLAGS = \
$(all_includes) \
-I$(top_srcdir)/include \
-I$(top_builddir)/include \
-I$(top_srcdir) \
$(NULL)
AM_CFLAGS = \
-Wall \
-ggdb3 \
$(LIBOSMOCORE_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
AM_LDFLAGS = \
$(LIBOSMOCORE_LIBS) \
$(COVERAGE_LDFLAGS) \
-no-install \
$(NULL)
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 \
sdp_msg_test \
$(NULL)
sdp_fmtp_test_SOURCES = \
sdp_fmtp_test.c \
$(NULL)
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)
sdp_msg_test_SOURCES = \
sdp_msg_test.c \
$(NULL)
sdp_msg_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
$(builddir)/sdp_msg_test >$(srcdir)/sdp_msg_test.ok 2>$(srcdir)/sdp_msg_test.err

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

@@ -0,0 +1,645 @@
#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_list.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);
}
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);
printf("osmo_sdp_codec_list_first() = %s\n",
osmo_sdp_codec_to_str_c(print_ctx, osmo_sdp_codec_list_first(codec_list)));
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));
}
printf("osmo_sdp_codec_list_first() = %s\n",
osmo_sdp_codec_to_str_c(print_ctx, osmo_sdp_codec_list_first(codec_list)));
report(list_ctx);
printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
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("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
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("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
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("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
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("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
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_to_str_c(summarize=true):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
rc = osmo_sdp_codec_list_move_to_first(codec_list, &all_codecs[0], &osmo_sdp_codec_cmp_equivalent);
printf("- osmo_sdp_codec_list_move_to_first('%s', equivalent) = %d\n",
osmo_sdp_codec_to_str_c(print_ctx, &all_codecs[0]), 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_to_str_c(summarize=true):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
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);
printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
talloc_free(print_ctx);
talloc_free(list_ctx);
}
static struct osmo_sdp_codec_list *init_codec_list(void *ctx, const struct osmo_sdp_codec *init_array)
{
struct osmo_sdp_codec_list *dst = osmo_sdp_codec_list_alloc(ctx);
for (; osmo_sdp_codec_is_set(init_array); init_array++)
osmo_sdp_codec_list_add(dst, init_array, NULL, false);
return dst;
}
void test_codec_list_cmp(void)
{
void *ctx = talloc_named_const(test_ctx, 0, __func__);
void *print_ctx = talloc_named_const(test_ctx, 0, "print");
int i;
const struct osmo_sdp_codec codec_a[] = {
{ .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 },
{}
};
const struct osmo_sdp_codec codec_b[][5] = {
/* same */
{
{ .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 },
},
/* different payload_type */
{
{ .payload_type = 96, .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 },
},
/* AMR fmtp in different order */
{
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "mode-set=0,2,4;octet-align=1" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
},
/* different AMR mode-set */
{
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=7" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
},
/* empty AMR mode-set */
{
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
},
/* different AMR octet-align */
{
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=0;mode-set=0,2,4" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
},
/* omitted AMR octet-align is identical to octet-align=0 */
{
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "mode-set=0,2,4" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
},
/* different order */
{
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
},
/* one less item */
{
{ .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 },
},
/* one more item */
{
{ .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 },
{ .payload_type = 110, .encoding_name = "GSM-EFR", .rate = 8000 },
},
};
const struct osmo_sdp_codec_cmp_flags *test_cmpf[] = {
&osmo_sdp_codec_cmp_name,
&osmo_sdp_codec_cmp_equivalent,
&osmo_sdp_codec_cmp_exact,
};
printf("\n\n--- %s()\n", __func__);
for (i = 0; i < ARRAY_SIZE(codec_b); i++) {
struct osmo_sdp_codec_list *list_a = init_codec_list(ctx, codec_a);
struct osmo_sdp_codec_list *list_b = init_codec_list(ctx, codec_b[i]);
int j;
printf("A = %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, list_a, false));
printf("B = %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, list_b, false));
for (j = 0; j < ARRAY_SIZE(test_cmpf); j++) {
const struct osmo_sdp_codec_cmp_flags *cmpf = test_cmpf[j];
int cmp = osmo_sdp_codec_list_cmp(list_a, list_b, cmpf);
int reverse_cmp = osmo_sdp_codec_list_cmp(list_b, list_a, cmpf);
printf(" cmpf[%d]: payload_type=%s rate=%s fmtp=%d: A %s B %s A\n",
j,
cmpf->payload_type ? "true" : "false",
cmpf->rate ? "true" : "false",
cmpf->fmtp,
(cmp == 0) ? "=="
: ((cmp < 0) ? "<" : ">"),
(reverse_cmp == 0) ? "=="
: ((reverse_cmp < 0) ? "<" : ">"));
if (reverse_cmp != -cmp)
printf(" *** ERROR: osmo_sdp_codec_list_cmp(reverse args) == %d, expected %d\n",
reverse_cmp, -cmp);
}
talloc_free(list_a);
talloc_free(list_b);
if (talloc_total_blocks(ctx) != 1) {
printf("ERROR: memleak:\n");
report(ctx);
}
printf("\n");
talloc_free_children(print_ctx);
}
talloc_free(print_ctx);
talloc_free(ctx);
}
void test_codec_list_intersection(void)
{
void *ctx = talloc_named_const(test_ctx, 0, __func__);
void *print_ctx = talloc_named_const(test_ctx, 0, "print");
int i;
const struct osmo_sdp_codec codec_a[] = {
{ .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 },
{}
};
const struct osmo_sdp_codec codec_b[][5] = {
/* same */
{
{ .payload_type = 96, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
{ .payload_type = 97, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 98, .encoding_name = "GSM-HR-08", .rate = 8000 },
},
/* same in different order */
{
{ .payload_type = 98, .encoding_name = "GSM-HR-08", .rate = 8000 },
{ .payload_type = 96, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
{ .payload_type = 97, .encoding_name = "GSM", .rate = 8000 },
},
/* two matches */
{
{ .payload_type = 98, .encoding_name = "GSM-HR-08", .rate = 8000 },
{ .payload_type = 110, .encoding_name = "GSM-EFR", .rate = 8000 },
{ .payload_type = 97, .encoding_name = "GSM", .rate = 8000 },
},
/* no match */
{
{ .payload_type = 97, .encoding_name = "AMR-WB", .rate = 16000 },
{ .payload_type = 98, .encoding_name = "FOO", .rate = 8000 },
{ .payload_type = 110, .encoding_name = "GSM-EFR", .rate = 8000 },
},
};
printf("\n\n--- %s()\n", __func__);
for (i = 0; i < ARRAY_SIZE(codec_b); i++) {
struct osmo_sdp_codec_list *list_a = init_codec_list(ctx, codec_a);
struct osmo_sdp_codec_list *list_b = init_codec_list(ctx, codec_b[i]);
printf("A = %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, list_a, false));
printf("B = %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, list_b, false));
osmo_sdp_codec_list_intersection(list_a, list_b, &osmo_sdp_codec_cmp_equivalent, false);
printf("osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=false)\n = %s\n",
osmo_sdp_codec_list_to_str_c(print_ctx, list_a, false));
talloc_free(list_a);
list_a = init_codec_list(ctx, codec_a);
osmo_sdp_codec_list_intersection(list_a, list_b, &osmo_sdp_codec_cmp_equivalent, true);
printf("osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=true)\n = %s\n",
osmo_sdp_codec_list_to_str_c(print_ctx, list_a, false));
talloc_free(list_a);
list_a = init_codec_list(ctx, codec_a);
osmo_sdp_codec_list_intersection(list_b, list_a, &osmo_sdp_codec_cmp_equivalent, false);
printf("osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=false)\n = %s\n",
osmo_sdp_codec_list_to_str_c(print_ctx, list_b, false));
talloc_free(list_a);
list_a = init_codec_list(ctx, codec_a);
osmo_sdp_codec_list_intersection(list_b, list_a, &osmo_sdp_codec_cmp_equivalent, true);
printf("osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=true)\n = %s\n",
osmo_sdp_codec_list_to_str_c(print_ctx, list_b, false));
talloc_free(list_a);
talloc_free(list_b);
if (talloc_total_blocks(ctx) != 1) {
printf("ERROR: memleak:\n");
report(ctx);
}
printf("\n");
talloc_free_children(print_ctx);
}
talloc_free(print_ctx);
talloc_free(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;
}
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;
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);
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");
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_codec_list,
test_codec_list_cmp,
test_codec_list_intersection,
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

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

@@ -0,0 +1,538 @@
--- 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_codec_list()
osmo_sdp_codec_list_first() = NULL
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
osmo_sdp_codec_list_first() = AMR:octet-align=1;mode-set=0,2,4#112
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
osmo_sdp_codec_list_to_str_c(summarize=true):
'AMR#112 GSM#3 GSM-HR-08#111'
osmo_sdp_codec_list_to_str_c(summarize=false):
'AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111'
- 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
osmo_sdp_codec_list_to_str_c(summarize=true):
'AMR#112 GSM#3 GSM-HR-08#111'
osmo_sdp_codec_list_to_str_c(summarize=false):
'AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111'
- 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
osmo_sdp_codec_list_to_str_c(summarize=true):
'2*AMR#112 2*GSM#3 2*GSM-HR-08#111'
osmo_sdp_codec_list_to_str_c(summarize=false):
'AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111'
- 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
osmo_sdp_codec_list_to_str_c(summarize=true):
'3*AMR 3*GSM 3*GSM-HR-08'
osmo_sdp_codec_list_to_str_c(summarize=false):
'AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98'
- 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
osmo_sdp_codec_list_to_str_c(summarize=true):
'3*AMR 3*GSM-HR-08 GSM#97'
osmo_sdp_codec_list_to_str_c(summarize=false):
'AMR:octet-align=1;mode-set=0,2,4#112 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98'
- 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_to_str_c(summarize=true):
'3*AMR 3*GSM-HR-08'
osmo_sdp_codec_list_to_str_c(summarize=false):
'AMR:octet-align=1;mode-set=0,2,4#112 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#96 GSM-HR-08#98'
- osmo_sdp_codec_list_move_to_first('AMR:octet-align=1;mode-set=0,2,4#112', equivalent) = 3
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[1] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[2] = AMR:octet-align=1;mode-set=0,2,4#96
codec_list[3] = GSM-HR-08#111
codec_list[4] = GSM-HR-08#111
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_to_str_c(summarize=true):
'3*AMR 3*GSM-HR-08'
osmo_sdp_codec_list_to_str_c(summarize=false):
'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#96 GSM-HR-08#111 GSM-HR-08#111 GSM-HR-08#98'
- osmo_sdp_codec_list_free_items()
0 entries
list_ctx
| 2 test_codec_list
| 1 struct osmo_sdp_codec_list
osmo_sdp_codec_list_to_str_c(summarize=true):
'(no-codecs)'
osmo_sdp_codec_list_to_str_c(summarize=false):
'(no-codecs)'
--- test_codec_list_cmp()
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
cmpf[1]: payload_type=false rate=true fmtp=1: A == B == A
cmpf[2]: payload_type=true rate=true fmtp=2: A == B == A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:octet-align=1;mode-set=0,2,4#96 GSM#3 GSM-HR-08#111
cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
cmpf[1]: payload_type=false rate=true fmtp=1: A == B == A
cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:mode-set=0,2,4;octet-align=1#112 GSM#3 GSM-HR-08#111
cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
cmpf[1]: payload_type=false rate=true fmtp=1: A == B == A
cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:octet-align=1;mode-set=7#112 GSM#3 GSM-HR-08#111
cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
cmpf[1]: payload_type=false rate=true fmtp=1: A < B > A
cmpf[2]: payload_type=true rate=true fmtp=2: A < B > A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:octet-align=1#112 GSM#3 GSM-HR-08#111
cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
cmpf[1]: payload_type=false rate=true fmtp=1: A == B == A
cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:octet-align=0;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
cmpf[1]: payload_type=false rate=true fmtp=1: A > B < A
cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
cmpf[1]: payload_type=false rate=true fmtp=1: A > B < A
cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = GSM#3 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112
cmpf[0]: payload_type=false rate=false fmtp=0: A < B > A
cmpf[1]: payload_type=false rate=true fmtp=1: A < B > A
cmpf[2]: payload_type=true rate=true fmtp=2: A < B > A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3
cmpf[0]: payload_type=false rate=false fmtp=0: A > B < A
cmpf[1]: payload_type=false rate=true fmtp=1: A > B < A
cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111 GSM-EFR#110
cmpf[0]: payload_type=false rate=false fmtp=0: A < B > A
cmpf[1]: payload_type=false rate=true fmtp=1: A < B > A
cmpf[2]: payload_type=true rate=true fmtp=2: A < B > A
--- test_codec_list_intersection()
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=false)
= AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=true)
= AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=false)
= AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=true)
= AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = GSM-HR-08#98 AMR:octet-align=1;mode-set=0,2,4#96 GSM#97
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=false)
= AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=true)
= AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=false)
= GSM-HR-08#98 AMR:octet-align=1;mode-set=0,2,4#96 GSM#97
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=true)
= GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112 GSM#3
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = GSM-HR-08#98 GSM-EFR#110 GSM#97
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=false)
= GSM#3 GSM-HR-08#111
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=true)
= GSM#97 GSM-HR-08#98
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=false)
= GSM-HR-08#98 GSM#97
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=true)
= GSM-HR-08#111 GSM#3
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR-WB/16000#97 FOO#98 GSM-EFR#110
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=false)
= (no-codecs)
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=true)
= (no-codecs)
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=false)
= (no-codecs)
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=true)
= (no-codecs)
--- test_obj_members()
o->codec = AMR:octet-align=1#96
ctx
| 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

126
tests/sdp/sdp_fmtp_test.c Normal file
View File

@@ -0,0 +1,126 @@
#include <inttypes.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/application.h>
#include <osmocom/core/logging.h>
#include <osmocom/sdp/fmtp.h>
struct get_val_test {
const char *fmtp_string;
const char *val_name;
bool expect_rc;
const char *expect_val;
};
const struct get_val_test get_val_tests[] = {
{
"foo=123;bar=success;baz=456", "foo",
true, "123"
},
{
"foo=123;bar=success;baz=456", "bar",
true, "success"
},
{
"foo=123;bar=success;baz=456", "baz",
true, "456"
},
};
void test_get_val(void)
{
int i;
printf("\n--- %s()\n", __func__);
for (i = 0; i < ARRAY_SIZE(get_val_tests); i++) {
const struct get_val_test *t = &get_val_tests[i];
char val[128] = {};
bool rc = osmo_sdp_fmtp_get_val(val, sizeof(val), t->fmtp_string, t->val_name);
bool ok;
printf("osmo_sdp_fmtp_get_val('%s', '%s') rc=%s",
t->fmtp_string, t->val_name,
rc ? "true" : "false");
if (rc)
printf(" val='%s'", val);
ok = true;
if (rc != t->expect_rc) {
printf(" ERROR: expected rc=%s", t->expect_rc ? "true" : "false");
ok = false;
}
if (t->expect_val && strcmp(val, t->expect_val)) {
printf(" ERROR: expected val='%s'", t->expect_val);
ok = false;
}
if (ok)
printf(" ok");
printf("\n");
}
printf("\n--- %s() DONE\n", __func__);
}
struct get_int_test {
const char *fmtp_string;
const char *val_name;
int64_t defval;
int64_t expect_rc;
};
const struct get_int_test get_int_tests[] = {
{
"foo=123;bar=success;baz=456", "foo", -1,
123
},
{
"foo=123;bar=success;baz=456", "bar", -1,
-1
},
{
"foo=123;bar=success;baz=456", "baz", -1,
456
},
};
void test_get_int(void)
{
int i;
printf("\n--- %s()\n", __func__);
for (i = 0; i < ARRAY_SIZE(get_int_tests); i++) {
const struct get_int_test *t = &get_int_tests[i];
int64_t rc = osmo_sdp_fmtp_get_int(t->fmtp_string, t->val_name, t->defval);
printf("osmo_sdp_fmtp_get_int('%s', '%s') rc=%"PRId64,
t->fmtp_string, t->val_name, rc);
if (rc != t->expect_rc) {
printf(" ERROR: expected rc=%"PRId64, t->expect_rc);
}
else {
printf(" ok");
}
printf("\n");
}
printf("\n--- %s() DONE\n", __func__);
}
static const struct log_info_cat log_categories[] = {
};
const struct log_info log_info = {
.cat = log_categories,
.num_cat = ARRAY_SIZE(log_categories),
};
int main(void)
{
void *ctx = talloc_named_const(NULL, 1, "sdp_fmtp_test");
osmo_init_logging2(ctx, &log_info);
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
log_set_print_timestamp(osmo_stderr_target, 0);
log_set_use_color(osmo_stderr_target, 0);
log_set_print_category_hex(osmo_stderr_target, 0);
log_set_print_category(osmo_stderr_target, 1);
test_get_val();
test_get_int();
return 0;
}

View File

View File

@@ -0,0 +1,14 @@
--- test_get_val()
osmo_sdp_fmtp_get_val('foo=123;bar=success;baz=456', 'foo') rc=true val='123' ok
osmo_sdp_fmtp_get_val('foo=123;bar=success;baz=456', 'bar') rc=true val='success' ok
osmo_sdp_fmtp_get_val('foo=123;bar=success;baz=456', 'baz') rc=true val='456' ok
--- test_get_val() DONE
--- test_get_int()
osmo_sdp_fmtp_get_int('foo=123;bar=success;baz=456', 'foo') rc=123 ok
osmo_sdp_fmtp_get_int('foo=123;bar=success;baz=456', 'bar') rc=-1 ok
osmo_sdp_fmtp_get_int('foo=123;bar=success;baz=456', 'baz') rc=456 ok
--- test_get_int() DONE

769
tests/sdp/sdp_msg_test.c Normal file
View File

@@ -0,0 +1,769 @@
#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_msg.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);
}
static void dump_sdp(const char *str, const char *prefix)
{
while (str && *str) {
const char *line_end = str;
while (*line_end && *line_end != '\r' && *line_end != '\n')
line_end++;
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_msg_test_data {
const char *sdp_input;
const char *expect_sdp_str;
};
static const struct sdp_msg_test_data sdp_msg_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=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=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"
/* The rtcp line is dropped, not supported yet */
},
{
"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=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 ends up further below */
/* The rtcp line is dropped, not supported yet */
"a=ptime:20\r\n"
"a=sendrecv\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=recvonly\r\n"
"a=rtcp:30437\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=recvonly ends up further below */
/* The rtcp line is dropped, not supported yet */
"a=ptime:20\r\n"
"a=recvonly\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=ptime:20\r\n"
"a=sendonly\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=ptime:20\r\n"
"a=inactive\r\n"
,
},
};
static void test_parse_and_compose(void)
{
void *ctx = talloc_named_const(test_ctx, 0, __func__);
void *print_ctx = talloc_named_const(test_ctx, 0, "print");
int i;
bool ok = true;
printf("\n\n%s\n", __func__);
for (i = 0; i < ARRAY_SIZE(sdp_msg_tests); i++) {
const struct sdp_msg_test_data *t = &sdp_msg_tests[i];
struct osmo_sdp_msg *sdp_msg;
char str[1024];
const char *expect;
struct osmo_sdp_msg_decode_ret ret;
printf("\n[%d]\n", i);
dump_sdp(t->sdp_input, "sdp input: ");
sdp_msg = osmo_sdp_msg_decode(ctx, t->sdp_input, -1, &ret);
if (ret.rc) {
printf("ERROR: %s at %s\n", strerror(abs(ret.rc)),
osmo_quote_cstr_c(print_ctx, ret.error.at_input_str, ret.error.at_input_str_len));
ok = false;
}
printf("parsed SDP message %s\n", osmo_sdp_msg_to_str_c(print_ctx, sdp_msg, false));
osmo_sdp_msg_encode_buf(str, sizeof(str), sdp_msg);
dump_sdp(str, "osmo_sdp_msg_encode_buf: ");
expect = t->expect_sdp_str ? : t->sdp_input;
if (strcmp(str, expect)) {
int j;
ok = false;
printf("ERROR:\n");
dump_sdp(expect, "expect: ");
for (j = 0; expect[j]; j++) {
if (expect[j] != str[j]) {
printf("ERROR at position %d, at:\n", j);
dump_sdp(str + j, " mismatch: ");
break;
}
}
} else
printf("[%d] ok\n", i);
report(ctx);
printf("talloc_free(sdp_msg)\n");
talloc_free(sdp_msg);
report(ctx);
if (talloc_total_blocks(ctx) != 1) {
printf("ERROR: memleak\n");
talloc_free_children(ctx);
}
printf("\n");
}
OSMO_ASSERT(ok);
talloc_free(ctx);
talloc_free(print_ctx);
}
struct intersect_test_data {
const char *descr;
const char *sdp_msg_a;
const char *sdp_msg_b;
const char *expect_intersection;
};
#define SDP_1 \
"v=0\r\n" \
"o=libosmo-sdp 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=libosmo-sdp 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=libosmo-sdp 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 123\r\n" \
"a=rtpmap:3 GSM/8000\r\n" \
"a=rtpmap:111 GSM-HR-08/8000\r\n" \
"a=rtpmap:123 FOO/8000\r\n" \
"a=ptime:20\r\n"
#define SDP_4 \
"v=0\r\n" \
"o=libosmo-sdp 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"
#define SDP_5 \
"v=0\r\n" \
"o=libosmo-sdp 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"
static const struct intersect_test_data intersect_tests[] = {
{
.descr = "identical codecs lead to no change",
.sdp_msg_a = SDP_1,
.sdp_msg_b =
"v=0\r\n"
"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"
,
.expect_intersection = SDP_1,
},
{
.descr = "identical codecs in different order also lead to no change",
.sdp_msg_a = SDP_1,
.sdp_msg_b =
"v=0\r\n"
"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"
,
.expect_intersection = SDP_1,
},
{
.descr = "identical codecs with mismatching payload type numbers also lead to no change",
.sdp_msg_a = SDP_1,
.sdp_msg_b =
"v=0\r\n"
"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"
,
.expect_intersection = SDP_1,
},
{
.descr = "identical codecs plus some extra codecs also lead to no change" ,
.sdp_msg_a = SDP_1,
.sdp_msg_b =
"v=0\r\n"
"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"
,
.expect_intersection = SDP_1,
},
{
.descr = "some codecs removed",
.sdp_msg_a = SDP_1,
.sdp_msg_b = SDP_2,
.expect_intersection = SDP_2,
},
{
.descr = "other codecs removed",
.sdp_msg_a = SDP_1,
.sdp_msg_b = SDP_3,
.expect_intersection = SDP_4,
},
{
.descr = "all codecs removed",
.sdp_msg_a = SDP_1,
.sdp_msg_b =
"v=0\r\n"
"s=empty"
,
.expect_intersection =
"v=0\r\n"
"o=libosmo-sdp 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"
},
{
.descr = "some real world test case",
.sdp_msg_a = SDP_5,
.sdp_msg_b = SDP_5,
.expect_intersection = SDP_5,
},
};
static const char *sdp_msg_logstr(const struct osmo_sdp_msg *sdp_msg)
{
static char buf[1024];
osmo_sdp_msg_encode_buf(buf, sizeof(buf), sdp_msg);
return buf;
}
static void test_intersect(void)
{
int i;
bool ok = true;
void *ctx = talloc_named_const(test_ctx, 0, __func__);
printf("\n\n%s\n", __func__);
for (i = 0; i < ARRAY_SIZE(intersect_tests); i++) {
const struct intersect_test_data *t = &intersect_tests[i];
struct osmo_sdp_msg *sdp_msg_a = NULL;
struct osmo_sdp_msg *sdp_msg_b = NULL;
char str[1024];
printf("\n[%d] %s\n", i, t->descr);
dump_sdp(t->sdp_msg_a, "SDP A: ");
dump_sdp(t->sdp_msg_b, " SDP B: ");
sdp_msg_a = osmo_sdp_msg_decode(ctx, t->sdp_msg_a, -1, NULL);
if (!sdp_msg_a) {
printf("ERROR parsing SDP A\n");
break;
}
dump_sdp(sdp_msg_logstr(sdp_msg_a), "parsed SDP A: ");
struct osmo_sdp_msg_decode_ret r;
sdp_msg_b = osmo_sdp_msg_decode(ctx, t->sdp_msg_b, -1, &r);
if (!sdp_msg_b) {
printf("ERROR parsing SDP B\n");
break;
}
dump_sdp(sdp_msg_logstr(sdp_msg_b), "parsed SDP B: ");
osmo_sdp_codec_list_intersection(sdp_msg_a->codecs, sdp_msg_b->codecs,
&osmo_sdp_codec_cmp_equivalent,
false);
osmo_sdp_msg_encode_buf(str, sizeof(str), sdp_msg_a);
dump_sdp(str, "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);
report(ctx);
printf("talloc_free(sdp_msg_a)\n");
talloc_free(sdp_msg_a);
report(ctx);
printf("talloc_free(sdp_msg_b)\n");
talloc_free(sdp_msg_b);
report(ctx);
if (talloc_total_blocks(ctx) != 1) {
printf("ERROR: memleak\n");
talloc_free_children(ctx);
}
printf("\n");
}
OSMO_ASSERT(ok);
talloc_free(ctx);
}
struct sdp_select_test_data {
const char *sdp;
const struct osmo_sdp_codec_cmp_flags *cmpf;
const struct osmo_sdp_codec select;
const char *expect_sdp;
};
static const struct osmo_sdp_codec_cmp_flags pt_only = { .payload_type = true };
static const struct sdp_select_test_data sdp_select_tests[] = {
{
"v=0\r\n"
"o=libosmo-sdp 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"
,
&pt_only,
{ .payload_type = 112, },
NULL
},
{
"v=0\r\n"
"o=libosmo-sdp 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"
,
&pt_only,
{ .payload_type = 3, },
"v=0\r\n"
"o=libosmo-sdp 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=libosmo-sdp 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"
,
&pt_only,
{ .payload_type = 111, },
"v=0\r\n"
"o=libosmo-sdp 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=libosmo-sdp 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"
,
&pt_only,
{ .payload_type = 110, },
"v=0\r\n"
"o=libosmo-sdp 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(void)
{
int i;
bool ok = true;
void *ctx = talloc_named_const(test_ctx, 0, __func__);
void *print_ctx = talloc_named_const(test_ctx, 0, "print");
printf("\n\n%s\n", __func__);
for (i = 0; i < ARRAY_SIZE(sdp_select_tests); i++) {
const struct sdp_select_test_data *t = &sdp_select_tests[i];
struct osmo_sdp_msg *sdp_msg;
char buf[1024];
const char *expect_sdp;
printf("\n[%d]\n", i);
sdp_msg = osmo_sdp_msg_decode(ctx, t->sdp, -1, NULL);
if (!sdp_msg) {
printf("ERROR parsing SDP\n");
break;
}
printf("SDP: %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, sdp_msg->codecs, false));
printf("Select: %s\n", osmo_sdp_codec_to_str_c(print_ctx, &t->select));
osmo_sdp_codec_list_move_to_first(sdp_msg->codecs, &t->select, t->cmpf);
printf("SDP: %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, sdp_msg->codecs, false));
osmo_sdp_msg_encode_buf(buf, sizeof(buf), sdp_msg);
expect_sdp = t->expect_sdp ? : t->sdp;
if (strcmp(buf, expect_sdp)) {
int j;
ok = false;
printf("ERROR:\n");
dump_sdp(buf, "selection result: ");
dump_sdp(expect_sdp, "expect result: ");
for (j = 0; expect_sdp[j]; j++) {
if (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);
report(ctx);
printf("talloc_free(sdp_msg)\n");
talloc_free(sdp_msg);
report(ctx);
if (talloc_total_blocks(ctx) != 1) {
printf("ERROR: memleak\n");
talloc_free_children(ctx);
}
printf("\n");
talloc_free_children(print_ctx);
}
OSMO_ASSERT(ok);
talloc_free(ctx);
talloc_free(print_ctx);
}
struct my_obj {
struct osmo_sdp_msg *sdp_msg;
};
static struct my_obj *my_obj_alloc(void *ctx)
{
struct my_obj *o = talloc_zero(ctx, struct my_obj);
return o;
}
static 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 my_obj *o;
printf("\n\n--- %s()\n", __func__);
o = my_obj_alloc(ctx);
o->sdp_msg = osmo_sdp_msg_alloc(o);
printf("o->sdp_msg = '%s'\n", osmo_sdp_msg_encode_c(print_ctx, o->sdp_msg));
report(ctx);
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 },
};
for (i = 0; i < ARRAY_SIZE(all_codecs); i++)
osmo_sdp_codec_list_add(o->sdp_msg->codecs, &all_codecs[i], false, false);
printf("o->sdp_msg = '%s'\n", osmo_sdp_msg_encode_c(print_ctx, o->sdp_msg));
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);
static const test_func_t test_func[] = {
test_parse_and_compose,
test_intersect,
test_select,
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

1321
tests/sdp/sdp_msg_test.ok Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -13,3 +13,24 @@ AT_KEYWORDS([mgcp])
cat $abs_srcdir/mgcp/mgcp_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/mgcp/mgcp_test], [], [expout], [ignore])
AT_CLEANUP
AT_SETUP([sdp_fmtp])
AT_KEYWORDS([sdp_fmtp])
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
AT_SETUP([sdp_msg])
AT_KEYWORDS([sdp_msg])
cat $abs_srcdir/sdp/sdp_msg_test.ok > expout
cat $abs_srcdir/sdp/sdp_msg_test.err > experr
AT_CHECK([$abs_top_builddir/tests/sdp/sdp_msg_test], [], [expout], [experr])
AT_CLEANUP