mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
synced 2025-10-23 08:12:01 +00:00
Compare commits
12 Commits
master
...
neels/sdp2
Author | SHA1 | Date | |
---|---|---|---|
|
2c42f44d7e | ||
|
53877bfb8e | ||
|
aca4ad4cce | ||
|
46c3882f75 | ||
|
5f8bad4b21 | ||
|
35357e80ee | ||
|
6c09836faa | ||
|
c43d76dc4d | ||
|
0051a29df9 | ||
|
ef5f1d3d7b | ||
|
c74c3d02c8 | ||
|
7338fd833b |
@@ -18,6 +18,7 @@ SUBDIRS = \
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = \
|
||||
libosmo-sdp.pc \
|
||||
libosmo-mgcp-client.pc \
|
||||
$(NULL)
|
||||
|
||||
|
@@ -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
15
debian/control
vendored
@@ -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
19
debian/copyright
vendored
@@ -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
4
debian/libosmo-sdp-dev.install
vendored
Normal 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
1
debian/libosmo-sdp1.install
vendored
Normal file
@@ -0,0 +1 @@
|
||||
usr/lib/*/libosmo-sdp.so.*
|
@@ -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 = \
|
||||
|
@@ -1,4 +1,5 @@
|
||||
SUBDIRS = \
|
||||
sdp \
|
||||
mgcp_client \
|
||||
mgcp \
|
||||
$(NULL)
|
||||
|
3
include/osmocom/sdp/Makefile.am
Normal file
3
include/osmocom/sdp/Makefile.am
Normal file
@@ -0,0 +1,3 @@
|
||||
noinst_HEADERS = \
|
||||
sdp_internal.h \
|
||||
$(NULL)
|
34
include/osmocom/sdp/fmtp.h
Normal file
34
include/osmocom/sdp/fmtp.h
Normal 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);
|
126
include/osmocom/sdp/sdp_codec.h
Normal file
126
include/osmocom/sdp/sdp_codec.h
Normal 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);
|
69
include/osmocom/sdp/sdp_codec_list.h
Normal file
69
include/osmocom/sdp/sdp_codec_list.h
Normal 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);
|
18
include/osmocom/sdp/sdp_internal.h
Normal file
18
include/osmocom/sdp/sdp_internal.h
Normal 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);
|
105
include/osmocom/sdp/sdp_msg.h
Normal file
105
include/osmocom/sdp/sdp_msg.h
Normal 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);
|
39
include/osmocom/sdp/sdp_strings.h
Normal file
39
include/osmocom/sdp/sdp_strings.h
Normal 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
10
libosmo-sdp.pc.in
Normal 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}/
|
@@ -15,6 +15,7 @@ AM_CFLAGS = \
|
||||
|
||||
# Libraries
|
||||
SUBDIRS = \
|
||||
libosmo-sdp \
|
||||
libosmo-mgcp-client \
|
||||
libosmo-mgcp \
|
||||
$(NULL)
|
||||
|
@@ -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)
|
||||
|
38
src/libosmo-sdp/Makefile.am
Normal file
38
src/libosmo-sdp/Makefile.am
Normal 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
193
src/libosmo-sdp/fmtp.c
Normal 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
251
src/libosmo-sdp/sdp_codec.c
Normal 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;
|
||||
}
|
367
src/libosmo-sdp/sdp_codec_list.c
Normal file
367
src/libosmo-sdp/sdp_codec_list.c
Normal 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);
|
||||
}
|
121
src/libosmo-sdp/sdp_internal.c
Normal file
121
src/libosmo-sdp/sdp_internal.c
Normal 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
517
src/libosmo-sdp/sdp_msg.c
Normal 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)
|
||||
}
|
@@ -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)
|
||||
|
@@ -1,6 +1,7 @@
|
||||
SUBDIRS = \
|
||||
mgcp_client \
|
||||
mgcp \
|
||||
sdp \
|
||||
$(NULL)
|
||||
|
||||
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
|
||||
|
@@ -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
63
tests/sdp/Makefile.am
Normal 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
645
tests/sdp/sdp_codec_test.c
Normal 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;
|
||||
}
|
0
tests/sdp/sdp_codec_test.err
Normal file
0
tests/sdp/sdp_codec_test.err
Normal file
538
tests/sdp/sdp_codec_test.ok
Normal file
538
tests/sdp/sdp_codec_test.ok
Normal 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
126
tests/sdp/sdp_fmtp_test.c
Normal 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;
|
||||
}
|
0
tests/sdp/sdp_fmtp_test.err
Normal file
0
tests/sdp/sdp_fmtp_test.err
Normal file
14
tests/sdp/sdp_fmtp_test.ok
Normal file
14
tests/sdp/sdp_fmtp_test.ok
Normal 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
769
tests/sdp/sdp_msg_test.c
Normal 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;
|
||||
}
|
0
tests/sdp/sdp_msg_test.err
Normal file
0
tests/sdp/sdp_msg_test.err
Normal file
1321
tests/sdp/sdp_msg_test.ok
Normal file
1321
tests/sdp/sdp_msg_test.ok
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
Reference in New Issue
Block a user