mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
synced 2025-11-02 21:13:44 +00:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e5ac26212a | ||
|
|
b86c401cf9 | ||
|
|
ab70b7f3fa | ||
|
|
8916c4fe39 | ||
|
|
094c3875f3 | ||
|
|
de8b171eda | ||
|
|
900d0c40fe | ||
|
|
e5bb708f38 | ||
|
|
158b4dfacc | ||
|
|
4782f8fa1d | ||
|
|
646126d0f9 | ||
|
|
e735cdd319 | ||
|
|
5c1e7e2f13 | ||
|
|
794235e53d | ||
|
|
b5618dade5 | ||
|
|
f84c0e681e | ||
|
|
9f23b7b5d5 | ||
|
|
01d2b902ad | ||
|
|
a263aeebc1 | ||
|
|
3a44c464f5 |
@@ -189,9 +189,11 @@ AC_OUTPUT(
|
||||
libosmo-mgcp-client.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 +201,7 @@ AC_OUTPUT(
|
||||
tests/atlocal
|
||||
tests/mgcp_client/Makefile
|
||||
tests/mgcp/Makefile
|
||||
tests/sdp/Makefile
|
||||
doc/Makefile
|
||||
doc/examples/Makefile
|
||||
doc/manuals/Makefile
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -13,6 +13,7 @@ struct mgcp_conn_rtp;
|
||||
void mgcp_codec_summary(struct mgcp_conn_rtp *conn);
|
||||
void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn);
|
||||
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param);
|
||||
int mgcp_codec_add2(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const char *fmtp);
|
||||
int mgcp_codec_decide(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst);
|
||||
const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn_rtp *conn,
|
||||
const char *subtype_name, unsigned int match_nr);
|
||||
|
||||
@@ -59,7 +59,7 @@ enum mgcp_x_osmo_ign {
|
||||
MGCP_X_OSMO_IGN_CALLID = 1,
|
||||
};
|
||||
|
||||
/* Codec parameters (communicated via SDP/fmtp) */
|
||||
/* Deprecated. Use the new fmtp string instead. */
|
||||
struct mgcp_codec_param {
|
||||
bool amr_octet_aligned_present;
|
||||
bool amr_octet_aligned;
|
||||
|
||||
@@ -84,8 +84,11 @@ struct mgcp_rtp_codec {
|
||||
char audio_name[64];
|
||||
char subtype_name[64];
|
||||
|
||||
/* Deprecated. Use the new fmtp string instead. */
|
||||
bool param_present;
|
||||
struct mgcp_codec_param param;
|
||||
|
||||
char fmtp[256];
|
||||
};
|
||||
|
||||
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
|
||||
|
||||
@@ -77,6 +77,9 @@ struct ptmap {
|
||||
|
||||
/*! payload type number (96-127) */
|
||||
unsigned int pt;
|
||||
|
||||
/*! the MGCP 'a=fmtp:N <...>' string, e.g. "mode-set=1,2,3;octet-align=0". */
|
||||
char fmtp[256];
|
||||
};
|
||||
|
||||
int ptmap_cmp(const struct ptmap *a, const struct ptmap *b);
|
||||
|
||||
@@ -57,10 +57,11 @@ struct mgcp_conn_peer {
|
||||
* address is set. If != MGCP_CONN_NONE, force this conn mode. */
|
||||
enum mgcp_connection_mode conn_mode;
|
||||
|
||||
/*! If the codec requires additional format parameters (fmtp), those cann be set here, see also
|
||||
* mgcp_common.h */
|
||||
bool param_present;
|
||||
struct mgcp_codec_param param;
|
||||
/*! Deprectated, use ptmap[].fmtp instead.
|
||||
* Global codec params. In case the codec requires additional format parameters (fmtp), those can be set
|
||||
* here, see also mgcp_common.h. The format parameters will be applied on all codecs where applicable. */
|
||||
bool param_present OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use ptmap[].fmtp instead");
|
||||
struct mgcp_codec_param param OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use ptmap[].fmtp instead");
|
||||
};
|
||||
|
||||
struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm_inst *parent_fi, uint32_t parent_term_evt,
|
||||
|
||||
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);
|
||||
11
include/osmocom/sdp/sdp_internal.h
Normal file
11
include/osmocom/sdp/sdp_internal.h
Normal file
@@ -0,0 +1,11 @@
|
||||
/* Internal header for non-public API shared across .c files */
|
||||
#pragma once
|
||||
|
||||
struct token {
|
||||
const char *start;
|
||||
const char *end;
|
||||
};
|
||||
|
||||
/* Copy a string from [start,end[, return as talloc allocated under ctx in *dst.
|
||||
* If *dst is non-NULL, talloc_free(*dst) first. */
|
||||
void token_copy(void *ctx, char **dst, const struct token *t);
|
||||
98
include/osmocom/sdp/sdp_msg.h
Normal file
98
include/osmocom/sdp/sdp_msg.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/* 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_err {
|
||||
int rc;
|
||||
/* 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;
|
||||
};
|
||||
|
||||
struct osmo_sdp_msg *osmo_sdp_msg_alloc(void *ctx);
|
||||
|
||||
struct osmo_sdp_msg *osmo_sdp_msg_decode(void *ctx, const char *src, struct osmo_sdp_err *err);
|
||||
|
||||
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"
|
||||
|
||||
@@ -15,6 +15,7 @@ AM_CFLAGS = \
|
||||
|
||||
# Libraries
|
||||
SUBDIRS = \
|
||||
libosmo-sdp \
|
||||
libosmo-mgcp-client \
|
||||
libosmo-mgcp \
|
||||
$(NULL)
|
||||
|
||||
@@ -40,6 +40,7 @@ libosmo_mgcp_client_la_LDFLAGS = \
|
||||
$(NULL)
|
||||
|
||||
libosmo_mgcp_client_la_LIBADD = \
|
||||
$(top_builddir)/src/libosmo-sdp/libosmo-sdp.la \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
#include <osmocom/mgcp_client/mgcp_client.h>
|
||||
#include <osmocom/mgcp_client/mgcp_client_internal.h>
|
||||
#include <osmocom/sdp/sdp_strings.h>
|
||||
|
||||
#include <osmocom/abis/e1_input.h>
|
||||
|
||||
@@ -388,11 +389,12 @@ static int mgcp_parse_audio_ptime_rtpmap(struct mgcp_response *r, const char *li
|
||||
{
|
||||
unsigned int pt;
|
||||
unsigned int i;
|
||||
char codec_resp[64];
|
||||
char codec_resp[256];
|
||||
int rc;
|
||||
|
||||
#define A_PTIME "a=ptime:"
|
||||
#define A_RTPMAP "a=rtpmap:"
|
||||
#define A_PTIME OSMO_SDP_A_PREFIX(OSMO_SDP_STR_PTIME)
|
||||
#define A_RTPMAP OSMO_SDP_A_PREFIX(OSMO_SDP_STR_RTPMAP)
|
||||
#define A_FMTP OSMO_SDP_A_PREFIX(OSMO_SDP_STR_FMTP)
|
||||
|
||||
if (osmo_str_startswith(line, A_PTIME)) {
|
||||
if (sscanf(line, A_PTIME "%u", &r->ptime) != 1) {
|
||||
@@ -401,7 +403,7 @@ static int mgcp_parse_audio_ptime_rtpmap(struct mgcp_response *r, const char *li
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (osmo_str_startswith(line, A_RTPMAP)) {
|
||||
if (sscanf(line, A_RTPMAP "%d %63s", &pt, codec_resp) != 2) {
|
||||
if (sscanf(line, A_RTPMAP "%d %255s", &pt, codec_resp) != 2) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Failed to parse SDP parameter, invalid rtpmap: %s\n", osmo_quote_str(line, -1));
|
||||
return -EINVAL;
|
||||
@@ -440,6 +442,42 @@ static int mgcp_parse_audio_ptime_rtpmap(struct mgcp_response *r, const char *li
|
||||
.codec = rc,
|
||||
};
|
||||
r->ptmap_len++;
|
||||
|
||||
} else if (osmo_str_startswith(line, A_FMTP)) {
|
||||
if (sscanf(line, A_FMTP "%d %255s", &pt, codec_resp) != 2) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Failed to parse SDP parameter, invalid fmtp: %s\n", osmo_quote_str(line, -1));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Earlier, a line like "m=audio 16002 RTP/AVP 98 112 3" established the desired order of payloads, now
|
||||
* enrich it with actual codec information provided by "a=rtpmap:..." entries.
|
||||
* For each, find the entry with the right pt number and add the info there. */
|
||||
|
||||
for (i = 0; i < r->ptmap_len; i++) {
|
||||
if (r->ptmap[i].pt != pt)
|
||||
continue;
|
||||
OSMO_STRLCPY_ARRAY(r->ptmap[r->ptmap_len].fmtp, codec_resp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* No entry was found. This is an error in the MGCP protocol, but let's just add another entry
|
||||
* anyway, to not make it look like it was never there. */
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"error in MGCP message: 'a=fmtp:%u' has no matching entry in 'm=audio ... %u'\n",
|
||||
pt, pt);
|
||||
if (r->ptmap_len >= ARRAY_SIZE(r->ptmap)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"cannot parse all codecs: can only store up to %zu rtpmap entries.\n",
|
||||
ARRAY_SIZE(r->ptmap));
|
||||
return -ENOSPC;
|
||||
}
|
||||
r->ptmap[r->ptmap_len] = (struct ptmap){
|
||||
.pt = pt,
|
||||
.codec = -1,
|
||||
};
|
||||
OSMO_STRLCPY_ARRAY(r->ptmap[r->ptmap_len].fmtp, codec_resp);
|
||||
r->ptmap_len++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -1382,13 +1420,20 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie
|
||||
MSGB_PRINTF_OR_RET("\r\n");
|
||||
|
||||
/* Add optional codec parameters (fmtp) */
|
||||
if (mgcp_msg->param_present) {
|
||||
for (i = 0; i < mgcp_msg->ptmap_len; i++) {
|
||||
/* The following is only applicable for AMR */
|
||||
if (mgcp_msg->ptmap[i].codec != CODEC_AMR_8000_1
|
||||
&& mgcp_msg->ptmap[i].codec != CODEC_AMRWB_16000_1)
|
||||
continue;
|
||||
for (i = 0; i < mgcp_msg->ptmap_len; i++) {
|
||||
/* Add fmtp string, if any is set. */
|
||||
if (mgcp_msg->ptmap[i].fmtp[0]) {
|
||||
MSGB_PRINTF_OR_RET("a=fmtp:%u %s\r\n", mgcp_msg->ptmap[i].pt, mgcp_msg->ptmap[i].fmtp);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* LEGACY COMPAT. Fill in fmtp with the legacy mgcp_msg->param, if any, when no individual fmtp is set
|
||||
* on the codec. We only ever supported AMR fmtp in mgcp_msg->param. */
|
||||
if (mgcp_msg->param_present
|
||||
&& (mgcp_msg->ptmap[i].codec == CODEC_AMR_8000_1
|
||||
|| mgcp_msg->ptmap[i].codec == CODEC_AMRWB_16000_1)) {
|
||||
pt = mgcp_msg->ptmap[i].pt;
|
||||
|
||||
if (mgcp_msg->param.amr_octet_aligned_present && mgcp_msg->param.amr_octet_aligned)
|
||||
MSGB_PRINTF_OR_RET("a=fmtp:%u octet-align=1\r\n", pt);
|
||||
else if (mgcp_msg->param.amr_octet_aligned_present && !mgcp_msg->param.amr_octet_aligned)
|
||||
@@ -1626,5 +1671,8 @@ int ptmap_cmp(const struct ptmap *a, const struct ptmap *b)
|
||||
rc = OSMO_CMP(a->codec, b->codec);
|
||||
if (rc)
|
||||
return rc;
|
||||
return OSMO_CMP(a->pt, b->pt);
|
||||
rc = OSMO_CMP(a->pt, b->pt);
|
||||
if (rc)
|
||||
return rc;
|
||||
return strcmp(a->fmtp, b->fmtp);
|
||||
}
|
||||
|
||||
@@ -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,13 @@ libosmo_mgcp_a_SOURCES = \
|
||||
mgcp_e1.c \
|
||||
mgcp_iuup.c \
|
||||
$(NULL)
|
||||
|
||||
libosmo_mgcp_la_LIBADD = \
|
||||
$(top_builddir)/src/libosmo-sdp/libosmo-sdp.la \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBOSMONETIF_LIBS) \
|
||||
$(LIBOSMOABIS_LIBS) \
|
||||
$(LIBOSMOTRAU_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
@@ -24,6 +24,10 @@
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_trunk.h>
|
||||
#include <osmocom/mgcp/mgcp_codec.h>
|
||||
|
||||
#include <osmocom/sdp/sdp_strings.h>
|
||||
#include <osmocom/sdp/fmtp.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
/* Helper function to dump codec information of a specified codec to a printable
|
||||
@@ -116,9 +120,9 @@ void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn)
|
||||
* \param[out] conn related rtp-connection.
|
||||
* \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined).
|
||||
* \param[in] audio_name audio codec name, in uppercase (e.g. "GSM/8000/1").
|
||||
* \param[in] param optional codec parameters (set to NULL when unused).
|
||||
* \param[in] fmtp optional codec parameters (set to NULL when unused).
|
||||
* \returns 0 on success, -EINVAL on failure. */
|
||||
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param)
|
||||
int mgcp_codec_add2(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const char *fmtp)
|
||||
{
|
||||
int rate;
|
||||
int channels;
|
||||
@@ -261,12 +265,16 @@ int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *aud
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy over optional codec parameters */
|
||||
if (param) {
|
||||
codec->param = *param;
|
||||
codec->param_present = true;
|
||||
} else
|
||||
codec->param_present = false;
|
||||
if (fmtp) {
|
||||
OSMO_STRLCPY_ARRAY(codec->fmtp, fmtp);
|
||||
if (strlen(codec->fmtp) != strlen(fmtp)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "fmtp too long: %zu > %zu\n", strlen(fmtp), strlen(codec->fmtp));
|
||||
/* let's just hope what is there is still useful, worst case the call's audio doesn't work */
|
||||
}
|
||||
}
|
||||
|
||||
/* legacy */
|
||||
codec->param_present = false;
|
||||
|
||||
conn->end.codecs_assigned++;
|
||||
return 0;
|
||||
@@ -276,23 +284,33 @@ error:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Return true if octet-aligned is set in the given codec. Default to octet-aligned=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-aligned
|
||||
* operation SHALL be used. If 0 or if not present,
|
||||
* bandwidth-efficient operation is employed.
|
||||
*
|
||||
* https://tools.ietf.org/html/rfc4867
|
||||
*/
|
||||
/*! Legacy compat, use mgcp_codec_add2() instead to be able to pass any fmtp besides AMR octet-align=1.
|
||||
* Add codec configuration depending on payload type and/or codec name. This
|
||||
* function uses the input parameters to extrapolate the full codec information.
|
||||
* \param[out] codec configuration (caller provided memory).
|
||||
* \param[out] conn related rtp-connection.
|
||||
* \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined).
|
||||
* \param[in] audio_name audio codec name, in uppercase (e.g. "GSM/8000/1").
|
||||
* \param[in] param optional codec parameters (set to NULL when unused).
|
||||
* \returns 0 on success, -EINVAL on failure. */
|
||||
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param)
|
||||
{
|
||||
const char *fmtp = NULL;
|
||||
if (param && param->amr_octet_aligned_present)
|
||||
fmtp = (param->amr_octet_aligned ? OSMO_SDP_STR_AMR_OCTET_ALIGN_1 : OSMO_SDP_STR_AMR_OCTET_ALIGN_0);
|
||||
|
||||
return mgcp_codec_add2(conn, payload_type, audio_name, fmtp);
|
||||
}
|
||||
|
||||
bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec)
|
||||
{
|
||||
if (!codec->param_present)
|
||||
return false;
|
||||
if (!codec->param.amr_octet_aligned_present)
|
||||
return false;
|
||||
return codec->param.amr_octet_aligned;
|
||||
/* Legacy */
|
||||
if (!codec->fmtp[0]
|
||||
&& codec->param_present
|
||||
&& codec->param.amr_octet_aligned_present)
|
||||
return codec->param.amr_octet_aligned;
|
||||
|
||||
return osmo_sdp_fmtp_amr_is_octet_aligned(codec->fmtp);
|
||||
}
|
||||
|
||||
/* Compare two codecs, all parameters must match up */
|
||||
@@ -471,13 +489,13 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn
|
||||
/* Check if the codec has a specific AMR mode (octet-aligned or bandwith-efficient) set. */
|
||||
bool mgcp_codec_amr_align_mode_is_indicated(const struct mgcp_rtp_codec *codec)
|
||||
{
|
||||
if (codec->param_present == false)
|
||||
return false;
|
||||
if (!codec->param.amr_octet_aligned_present)
|
||||
return false;
|
||||
if (strcmp(codec->subtype_name, "AMR") != 0)
|
||||
return false;
|
||||
return true;
|
||||
if (!codec->fmtp[0]) {
|
||||
/* Legacy */
|
||||
return codec->param_present && codec->param.amr_octet_aligned_present;
|
||||
}
|
||||
|
||||
/* Just check for presence, not the actual value. */
|
||||
return osmo_sdp_fmtp_get_val(NULL, 0, codec->fmtp, OSMO_SDP_STR_AMR_OCTET_ALIGN);
|
||||
}
|
||||
|
||||
/* Find the payload type number configured for a specific codec by SDP.
|
||||
|
||||
@@ -1207,11 +1207,12 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
|
||||
if (mgcp_conn_rtp_is_iuup(conn_dst) || mgcp_conn_rtp_is_iuup(conn_src)) {
|
||||
/* the iuup code will correctly transform to the correct AMR mode */
|
||||
} else if (mgcp_codec_amr_align_mode_is_indicated(conn_dst->end.codec)) {
|
||||
rc = amr_oa_bwe_convert(endp, msg, conn_dst->end.codec->param.amr_octet_aligned);
|
||||
bool oa = mgcp_codec_amr_is_octet_aligned(conn_dst->end.codec);
|
||||
rc = amr_oa_bwe_convert(endp, msg, oa);
|
||||
if (rc < 0) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR,
|
||||
"Error in AMR octet-aligned <-> bandwidth-efficient mode conversion (target=%s)\n",
|
||||
conn_dst->end.codec->param.amr_octet_aligned ? "octet-aligned" : "bandwidth-efficient");
|
||||
oa ? "octet-aligned" : "bandwidth-efficient");
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
@@ -1564,16 +1565,18 @@ static int rx_rtp(struct msgb *msg)
|
||||
if (mc->proto == MGCP_PROTO_RTP
|
||||
&& conn_src->end.codec
|
||||
&& mgcp_codec_amr_align_mode_is_indicated(conn_src->end.codec)) {
|
||||
bool src_oa;
|
||||
/* Make sure that the incoming AMR frame format matches the frame format that the call agent has
|
||||
* communicated via SDP when the connection was created/modfied. */
|
||||
int oa = amr_oa_check((char*)msgb_data(msg), msgb_length(msg));
|
||||
if (oa < 0)
|
||||
goto out_free;
|
||||
if (((bool)oa) != conn_src->end.codec->param.amr_octet_aligned) {
|
||||
src_oa = mgcp_codec_amr_is_octet_aligned(conn_src->end.codec);
|
||||
if (((bool)oa) != src_oa) {
|
||||
LOG_CONN_RTP(conn_src, LOGL_NOTICE,
|
||||
"rx_rtp(%u bytes): Expected RTP AMR octet-aligned=%u but got octet-aligned=%u."
|
||||
" check the config of your call-agent!\n",
|
||||
msgb_length(msg), conn_src->end.codec->param.amr_octet_aligned, oa);
|
||||
msgb_length(msg), src_oa, oa);
|
||||
goto out_free;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1083,17 +1083,6 @@ mgcp_header_done:
|
||||
|
||||
mgcp_rtp_end_config(endp, 0, &conn->end);
|
||||
|
||||
/* check connection mode setting */
|
||||
if (conn->conn->mode != MGCP_CONN_LOOPBACK
|
||||
&& conn->conn->mode != MGCP_CONN_RECV_ONLY
|
||||
&& osmo_sockaddr_port(&conn->end.addr.u.sa) == 0) {
|
||||
LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
|
||||
"CRCX: selected connection mode type requires an opposite end!\n");
|
||||
error_code = 527;
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC));
|
||||
goto error2;
|
||||
}
|
||||
|
||||
/* Find a local address for conn based on policy and initial SDP remote
|
||||
information, then find a free port for it */
|
||||
if (mgcp_get_local_addr(conn->end.local_addr, conn) < 0) {
|
||||
@@ -1131,6 +1120,7 @@ mgcp_header_done:
|
||||
|
||||
LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
|
||||
"CRCX: connection successfully created\n");
|
||||
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_SUCCESS));
|
||||
mgcp_endp_update(endp);
|
||||
|
||||
@@ -1295,17 +1285,6 @@ mgcp_header_done:
|
||||
if (conn->type == MGCP_RTP_DEFAULT && strcmp(conn->end.codec->subtype_name, "VND.3GPP.IUFP") == 0)
|
||||
rc = mgcp_conn_iuup_init(conn);
|
||||
|
||||
/* check connection mode setting */
|
||||
if (conn->conn->mode != MGCP_CONN_LOOPBACK
|
||||
&& conn->conn->mode != MGCP_CONN_RECV_ONLY
|
||||
&& !mgcp_rtp_end_remote_addr_available(&conn->end)) {
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
|
||||
"MDCX: selected connection mode type requires an opposite end!\n");
|
||||
error_code = 527;
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC));
|
||||
goto error3;
|
||||
}
|
||||
|
||||
if (mgcp_conn_rtp_is_osmux(conn)) {
|
||||
OSMO_ASSERT(conn->osmux.local_cid_allocated);
|
||||
if (remote_osmux_cid < -1) {
|
||||
|
||||
@@ -35,6 +35,9 @@
|
||||
#include <osmocom/mgcp/mgcp_sdp.h>
|
||||
#include <osmocom/mgcp/mgcp_protocol.h>
|
||||
|
||||
#include <osmocom/sdp/fmtp.h>
|
||||
#include <osmocom/sdp/sdp_strings.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
@@ -55,7 +58,7 @@ struct sdp_rtp_map {
|
||||
};
|
||||
struct sdp_fmtp_param {
|
||||
int payload_type;
|
||||
struct mgcp_codec_param param;
|
||||
const char *fmtp;
|
||||
};
|
||||
|
||||
|
||||
@@ -195,11 +198,7 @@ static int fmtp_from_sdp(void *ctx, struct sdp_fmtp_param *fmtp_param, char *sdp
|
||||
{
|
||||
char *str;
|
||||
char *str_ptr;
|
||||
char *param_str;
|
||||
unsigned int pt;
|
||||
unsigned int count = 0;
|
||||
char delimiter;
|
||||
unsigned int amr_octet_aligned;
|
||||
|
||||
memset(fmtp_param, 0, sizeof(*fmtp_param));
|
||||
|
||||
@@ -218,40 +217,13 @@ static int fmtp_from_sdp(void *ctx, struct sdp_fmtp_param *fmtp_param, char *sdp
|
||||
goto error;
|
||||
fmtp_param->payload_type = pt;
|
||||
|
||||
/* Advance pointer to the beginning of the parameter section and
|
||||
* tokenize string */
|
||||
/* Advance pointer to the beginning of the parameter section */
|
||||
str_ptr = strstr(str_ptr, " ");
|
||||
if (!str_ptr)
|
||||
goto error;
|
||||
str_ptr++;
|
||||
|
||||
param_str = strtok(str_ptr, " ");
|
||||
if (!param_str)
|
||||
goto exit;
|
||||
|
||||
while (1) {
|
||||
/* Make sure that we don't get trapped in an endless loop */
|
||||
if (count > 256)
|
||||
goto error;
|
||||
|
||||
/* Chop off delimiters ';' at the end */
|
||||
delimiter = str_ptr[strlen(str_ptr) - 1];
|
||||
if (delimiter == ';' || delimiter == ',')
|
||||
str_ptr[strlen(str_ptr) - 1] = '\0';
|
||||
|
||||
/* AMR octet aligned parameter (see also RFC 3267, section 8.3) */
|
||||
if (sscanf(param_str, "octet-align=%d", &amr_octet_aligned) == 1) {
|
||||
fmtp_param->param.amr_octet_aligned_present = true;
|
||||
fmtp_param->param.amr_octet_aligned = false;
|
||||
if (amr_octet_aligned == 1)
|
||||
fmtp_param->param.amr_octet_aligned = true;
|
||||
}
|
||||
|
||||
param_str = strtok(NULL, " ");
|
||||
if (!param_str)
|
||||
break;
|
||||
count++;
|
||||
}
|
||||
fmtp_param->fmtp = talloc_strdup(ctx, str_ptr);
|
||||
|
||||
exit:
|
||||
talloc_free(str);
|
||||
@@ -299,13 +271,13 @@ static int audio_ip_from_sdp(struct osmo_sockaddr *dst_addr, char *sdp)
|
||||
|
||||
/* Pick optional fmtp parameters by payload type, if there are no fmtp
|
||||
* parameters, a nullpointer is returned */
|
||||
static struct mgcp_codec_param *param_by_pt(int pt, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len)
|
||||
static const char *param_by_pt(int pt, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < fmtp_params_len; i++) {
|
||||
if (fmtp_params[i].payload_type == pt)
|
||||
return &fmtp_params[i].param;
|
||||
return fmtp_params[i].fmtp;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@@ -326,7 +298,6 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
unsigned int codecs_used = 0;
|
||||
struct sdp_fmtp_param fmtp_params[MGCP_MAX_CODECS];
|
||||
unsigned int fmtp_used = 0;
|
||||
struct mgcp_codec_param *codec_param;
|
||||
char ipbuf[INET6_ADDRSTRLEN];
|
||||
char *line;
|
||||
unsigned int i;
|
||||
@@ -421,8 +392,8 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
|
||||
/* Store parsed codec information */
|
||||
for (i = 0; i < codecs_used; i++) {
|
||||
codec_param = param_by_pt(codecs[i].payload_type, fmtp_params, fmtp_used);
|
||||
rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line, codec_param);
|
||||
const char *fmtp = param_by_pt(codecs[i].payload_type, fmtp_params, fmtp_used);
|
||||
rc = mgcp_codec_add2(conn, codecs[i].payload_type, codecs[i].map_line, fmtp);
|
||||
if (rc < 0)
|
||||
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "failed to add codec\n");
|
||||
}
|
||||
@@ -436,10 +407,12 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
if (codecs_used == 0)
|
||||
LOGPC(DLMGCP, LOGL_NOTICE, "none");
|
||||
for (i = 0; i < codecs_used; i++) {
|
||||
LOGPC(DLMGCP, LOGL_NOTICE, "%d=%s",
|
||||
LOGPC(DLMGCP, LOGL_NOTICE, " %d=%s%s%s%s",
|
||||
rtp->codecs[i].payload_type,
|
||||
strlen(rtp->codecs[i].subtype_name) ? rtp->codecs[i].subtype_name : "unknown");
|
||||
LOGPC(DLMGCP, LOGL_NOTICE, " ");
|
||||
strlen(rtp->codecs[i].subtype_name) ? rtp->codecs[i].subtype_name : "unknown",
|
||||
rtp->codecs[i].fmtp[0] ? ",fmtp='" : "",
|
||||
rtp->codecs[i].fmtp,
|
||||
rtp->codecs[i].fmtp[0] ? "'" : "");
|
||||
}
|
||||
LOGPC(DLMGCP, LOGL_NOTICE, "\n");
|
||||
|
||||
@@ -494,18 +467,15 @@ static int add_fmtp(struct msgb *sdp, struct sdp_fmtp_param *fmtp_params, unsign
|
||||
int rc;
|
||||
|
||||
for (i = 0; i < fmtp_params_len; i++) {
|
||||
bool first = true;
|
||||
rc = msgb_printf(sdp, "a=fmtp:%u", fmtp_params[i].payload_type);
|
||||
if (rc < 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* Add amr octet align parameter */
|
||||
if (fmtp_params[i].param.amr_octet_aligned_present) {
|
||||
if (fmtp_params[i].param.amr_octet_aligned)
|
||||
rc = msgb_printf(sdp, " octet-align=1");
|
||||
else
|
||||
rc = msgb_printf(sdp, " octet-align=0");
|
||||
if (rc < 0)
|
||||
return -EINVAL;
|
||||
if (fmtp_params[i].fmtp) {
|
||||
msgb_printf(sdp, "%s%s", first ? " " : ";", fmtp_params[i].fmtp);
|
||||
first = false;
|
||||
}
|
||||
|
||||
rc = msgb_printf(sdp, "\r\n");
|
||||
@@ -529,7 +499,6 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
||||
const struct mgcp_rtp_codec *codec;
|
||||
const char *audio_name;
|
||||
int payload_type;
|
||||
struct sdp_fmtp_param fmtp_param;
|
||||
int rc;
|
||||
int payload_types[1];
|
||||
int local_port;
|
||||
@@ -578,12 +547,22 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
||||
goto buffer_too_small;
|
||||
}
|
||||
|
||||
if (codec->param_present) {
|
||||
fmtp_param.payload_type = payload_type;
|
||||
fmtp_param.param = codec->param;
|
||||
fmtp_params[0] = fmtp_param;
|
||||
if (codec->fmtp[0]) {
|
||||
fmtp_params[0] = (struct sdp_fmtp_param){
|
||||
.payload_type = payload_type,
|
||||
.fmtp = codec->fmtp,
|
||||
};
|
||||
fmtp_params_len = 1;
|
||||
} else if (codec->param_present) {
|
||||
/* Legacy */
|
||||
fmtp_params[0] = (struct sdp_fmtp_param){
|
||||
.payload_type = payload_type,
|
||||
};
|
||||
fmtp_params_len = 1;
|
||||
fmtp_params[0].fmtp = (codec->param.amr_octet_aligned ?
|
||||
OSMO_SDP_STR_AMR_OCTET_ALIGN_1 : OSMO_SDP_STR_AMR_OCTET_ALIGN_0);
|
||||
}
|
||||
|
||||
rc = add_fmtp(sdp, fmtp_params, fmtp_params_len);
|
||||
if (rc < 0)
|
||||
goto buffer_too_small;
|
||||
|
||||
28
src/libosmo-sdp/Makefile.am
Normal file
28
src/libosmo-sdp/Makefile.am
Normal file
@@ -0,0 +1,28 @@
|
||||
AM_CPPFLAGS = \
|
||||
$(all_includes) \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(top_builddir) \
|
||||
$(NULL)
|
||||
|
||||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(TALLOC_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
noinst_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_LIBADD = \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(TALLOC_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);
|
||||
}
|
||||
47
src/libosmo-sdp/sdp_internal.c
Normal file
47
src/libosmo-sdp/sdp_internal.c
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <osmocom/sdp/sdp_internal.h>
|
||||
|
||||
/* Copy a string from t->start to t->end, return as talloc allocated under ctx in *dst.
|
||||
* If *dst is non-NULL, talloc_free(*dst) first. */
|
||||
void token_copy(void *ctx, char **dst, const struct token *t)
|
||||
{
|
||||
size_t len;
|
||||
if (*dst)
|
||||
talloc_free(*dst);
|
||||
if (!t->start || !(t->end > t->start)) {
|
||||
*dst = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
len = t->end - t->start;
|
||||
*dst = talloc_size(ctx, len + 1);
|
||||
osmo_strlcpy(*dst, t->start, len + 1);
|
||||
talloc_set_name_const(*dst, *dst);
|
||||
}
|
||||
471
src/libosmo-sdp/sdp_msg.c
Normal file
471
src/libosmo-sdp/sdp_msg.c
Normal file
@@ -0,0 +1,471 @@
|
||||
/* 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 <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. */
|
||||
const char *get_line_end(const char *src)
|
||||
{
|
||||
const char *line_end = strchr(src, '\r');
|
||||
if (!line_end)
|
||||
line_end = strchr(src, '\n');
|
||||
if (!line_end)
|
||||
line_end = src + strlen(src);
|
||||
return line_end;
|
||||
}
|
||||
|
||||
static bool str_is_attrib(const char *str, const char *attrib_name, char expect_next_char)
|
||||
{
|
||||
char next_c;
|
||||
if (!osmo_str_startswith(str, attrib_name))
|
||||
return false;
|
||||
next_c = str[strlen(attrib_name)];
|
||||
if (expect_next_char == next_c)
|
||||
return true;
|
||||
/* Treat \0 as equivalent with line end */
|
||||
if (!expect_next_char && (next_c == '\r' || next_c == '\n'))
|
||||
return true;
|
||||
/* It started with the string, but continued otherwise */
|
||||
return false;
|
||||
}
|
||||
|
||||
static enum osmo_sdp_media_direcion_e check_for_media_direction(const char *str)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(mdir_str); i++) {
|
||||
if (i == OSMO_SDP_MDIR_UNSET)
|
||||
continue;
|
||||
if (str_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;
|
||||
}
|
||||
|
||||
|
||||
/* 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'
|
||||
*/
|
||||
int sdp_parse_attrib(struct osmo_sdp_msg *sdp, const char *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);
|
||||
|
||||
if (str_is_attrib(src, OSMO_SDP_STR_RTPMAP, ':')) {
|
||||
/* "a=rtpmap:96 AMR/8000" */
|
||||
struct token audio_name;
|
||||
const char *slash;
|
||||
if (sscanf(src, OSMO_SDP_STR_RTPMAP ":%u", &payload_type) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
codec = find_or_create_payload_type(sdp, payload_type);
|
||||
|
||||
audio_name.start = strchr(src, ' ');
|
||||
if (!audio_name.start)
|
||||
return -EINVAL;
|
||||
audio_name.start++;
|
||||
if (audio_name.start >= get_line_end(src))
|
||||
return -EINVAL;
|
||||
|
||||
slash = strchr(audio_name.start, '/');
|
||||
|
||||
audio_name.end = slash ? : line_end;
|
||||
token_copy(codec, &codec->encoding_name, &audio_name);
|
||||
|
||||
if (audio_name.end >= line_end) {
|
||||
/* There should be a "/8000" here. If it is missing, let's not be strict about it. */
|
||||
codec->rate = 8000;
|
||||
} else {
|
||||
unsigned int channels = 1;
|
||||
if (sscanf(audio_name.end, "/%u/%u", &codec->rate, &channels) < 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (channels != 1)
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
|
||||
else if (str_is_attrib(src, OSMO_SDP_STR_FMTP, ':')) {
|
||||
/* "a=fmtp:112 octet-align=1;mode-set=0,1,2,3" */
|
||||
struct token fmtp_str;
|
||||
const char *line_end = get_line_end(src);
|
||||
if (sscanf(src, OSMO_SDP_STR_FMTP ":%u", &payload_type) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
codec = find_or_create_payload_type(sdp, payload_type);
|
||||
|
||||
fmtp_str.start = strchr(src, ' ');
|
||||
if (!fmtp_str.start)
|
||||
return -EINVAL;
|
||||
fmtp_str.start++;
|
||||
if (fmtp_str.start >= line_end)
|
||||
return -EINVAL;
|
||||
|
||||
fmtp_str.end = line_end;
|
||||
token_copy(codec, &codec->fmtp, &fmtp_str);
|
||||
}
|
||||
|
||||
else if (str_is_attrib(src, OSMO_SDP_STR_PTIME, ':')) {
|
||||
/* "a=ptime:20" */
|
||||
if (sscanf(src, OSMO_SDP_STR_PTIME ":%u", &sdp->ptime) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
}
|
||||
|
||||
/* "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 char *src)
|
||||
{
|
||||
unsigned int port;
|
||||
int i;
|
||||
const char *payload_type_str;
|
||||
const char *line_end = get_line_end(src);
|
||||
if (sscanf(src, "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 spaces on */
|
||||
payload_type_str = src;
|
||||
for (i = 0; i < 3; i++) {
|
||||
payload_type_str = strchr(payload_type_str, ' ');
|
||||
if (!payload_type_str)
|
||||
return -EINVAL;
|
||||
while (*payload_type_str == ' ')
|
||||
payload_type_str++;
|
||||
if (payload_type_str >= line_end)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Parse listing of payload type numbers after "RTP/AVP" */
|
||||
while (payload_type_str < line_end) {
|
||||
unsigned int payload_type;
|
||||
struct osmo_sdp_codec *codec;
|
||||
const char *encoding_name;
|
||||
if (sscanf(payload_type_str, "%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);
|
||||
|
||||
payload_type_str = strchr(payload_type_str, ' ');
|
||||
if (!payload_type_str)
|
||||
payload_type_str = line_end;
|
||||
while (*payload_type_str == ' ')
|
||||
payload_type_str++;
|
||||
}
|
||||
|
||||
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 char *src)
|
||||
{
|
||||
char ipv[10];
|
||||
char addr_str[INET6_ADDRSTRLEN];
|
||||
if (sscanf(src, "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);
|
||||
}
|
||||
|
||||
static void next_token(struct token *t, const char *str, const char *end)
|
||||
{
|
||||
t->start = str;
|
||||
while (*t->start == ' ' && t->start < end)
|
||||
t->start++;
|
||||
t->end = t->start;
|
||||
while (*t->end != ' ' && t->end < end)
|
||||
t->end++;
|
||||
}
|
||||
|
||||
/* 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 char *src)
|
||||
{
|
||||
struct token t;
|
||||
char addr_str[INET6_ADDRSTRLEN + 1] = {};
|
||||
const char *line_end = get_line_end(src);
|
||||
|
||||
next_token(&t, src, 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 char *src)
|
||||
{
|
||||
const char *line_end = get_line_end(src);
|
||||
if (sdp->session_name)
|
||||
talloc_free(sdp->session_name);
|
||||
if (line_end <= src)
|
||||
sdp->session_name = NULL;
|
||||
else
|
||||
sdp->session_name = talloc_strndup(sdp, src, line_end - src);
|
||||
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 err is non-NULL, details of the error are returned in err->*.
|
||||
*/
|
||||
struct osmo_sdp_msg *osmo_sdp_msg_decode(void *ctx, const char *src, struct osmo_sdp_err *err)
|
||||
{
|
||||
struct osmo_sdp_msg *sdp;
|
||||
const char *pos;
|
||||
|
||||
if (err)
|
||||
*err = (struct osmo_sdp_err){};
|
||||
|
||||
sdp = osmo_sdp_msg_alloc(ctx);
|
||||
|
||||
for (pos = src; pos && *pos; pos++) {
|
||||
char attrib;
|
||||
int rc = 0;
|
||||
|
||||
if (*pos == '\r' || *pos == '\n')
|
||||
continue;
|
||||
|
||||
/* Expecting only lines starting with 'X='. Not being too strict about it is probably alright. */
|
||||
if (pos[1] != '=')
|
||||
goto next_line;
|
||||
|
||||
attrib = *pos;
|
||||
pos += 2;
|
||||
switch (attrib) {
|
||||
/* a=... */
|
||||
case 'a':
|
||||
rc = sdp_parse_attrib(sdp, pos);
|
||||
break;
|
||||
case 'm':
|
||||
rc = sdp_parse_media_description(sdp, pos);
|
||||
break;
|
||||
case 'c':
|
||||
rc = sdp_parse_connection_info(sdp, pos);
|
||||
break;
|
||||
case 'o':
|
||||
rc = sdp_parse_origin(sdp, pos);
|
||||
break;
|
||||
case 's':
|
||||
rc = sdp_parse_session_name(sdp, pos);
|
||||
break;
|
||||
default:
|
||||
/* ignore any other parameters */
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc) {
|
||||
if (err) {
|
||||
const char *line_end = get_line_end(pos);
|
||||
/* shift back to include the 'x=' part as well */
|
||||
pos -= 2;
|
||||
*err = (struct osmo_sdp_err){
|
||||
.rc = rc,
|
||||
.at_input_str = pos,
|
||||
.at_input_str_len = line_end - pos,
|
||||
};
|
||||
}
|
||||
talloc_free(sdp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
next_line:
|
||||
pos = strstr(pos, "\r\n");
|
||||
if (!pos)
|
||||
break;
|
||||
}
|
||||
|
||||
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) \
|
||||
|
||||
@@ -103,7 +103,7 @@ static void test_strline(void)
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 16002 RTP/AVP 97\r\n" \
|
||||
"a=rtpmap:97 GSM-EFR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define MDCX3A_RET \
|
||||
"200 18983215 OK\r\n" \
|
||||
@@ -115,7 +115,7 @@ static void test_strline(void)
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 16002 RTP/AVP 97\r\n" \
|
||||
"a=rtpmap:97 GSM-EFR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define MDCX3_FMTP_RET \
|
||||
"200 18983215 OK\r\n" \
|
||||
@@ -127,40 +127,49 @@ static void test_strline(void)
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 16006 RTP/AVP 97\r\n" \
|
||||
"a=rtpmap:97 GSM-EFR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define MDCX4_ADDR0000 \
|
||||
"MDCX 18983216 1@mgw MGCP 1.0\r\n" \
|
||||
"M: sendrecv\r" \
|
||||
"M: sendrecv\r\n" \
|
||||
"C: 2\r\n" \
|
||||
"I: %s\r\n" \
|
||||
"L: p:20, a:AMR, nt:IN\r\n" \
|
||||
"\n" \
|
||||
"\r\n" \
|
||||
"v=0\r\n" \
|
||||
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
|
||||
"c=IN IP4 0.0.0.0\r\n" \
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 4441 RTP/AVP 99\r\n" \
|
||||
"a=rtpmap:99 AMR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define MDCX4_ADDR0000_RET \
|
||||
"527 18983216 FAIL\r\n"
|
||||
"200 18983216 OK\r\n" \
|
||||
"\r\n" \
|
||||
"v=0\r\n" \
|
||||
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
|
||||
"s=-\r\n" \
|
||||
"c=IN IP4 0.0.0.0\r\n" \
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 16002 RTP/AVP 99\r\n" \
|
||||
"a=rtpmap:99 AMR/8000\r\n" \
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define MDCX4 \
|
||||
"MDCX 18983217 1@mgw MGCP 1.0\r\n" \
|
||||
"M: sendrecv\r" \
|
||||
"M: sendrecv\r\n" \
|
||||
"C: 2\r\n" \
|
||||
"I: %s\r\n" \
|
||||
"L: p:20, a:AMR, nt:IN\r\n" \
|
||||
"\n" \
|
||||
"\r\n" \
|
||||
"v=0\r\n" \
|
||||
"o=- %s 23 IN IP4 5.6.7.8\r\n" \
|
||||
"c=IN IP4 5.6.7.8\r\n" \
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 4441 RTP/AVP 99\r\n" \
|
||||
"a=rtpmap:99 AMR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define MDCX4_RET(Ident) \
|
||||
"200 " Ident " OK\r\n" \
|
||||
@@ -172,7 +181,7 @@ static void test_strline(void)
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 16002 RTP/AVP 99\r\n" \
|
||||
"a=rtpmap:99 AMR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define MDCX4_RO_RET(Ident) \
|
||||
"200 " Ident " OK\r\n" \
|
||||
@@ -184,87 +193,87 @@ static void test_strline(void)
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 16002 RTP/AVP 112\r\n" \
|
||||
"a=rtpmap:112 AMR\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define MDCX4_PT1 \
|
||||
"MDCX 18983218 1@mgw MGCP 1.0\r\n" \
|
||||
"M: SENDRECV\r" \
|
||||
"M: SENDRECV\r\n" \
|
||||
"C: 2\r\n" \
|
||||
"I: %s\r\n" \
|
||||
"L: p:20-40, a:AMR, nt:IN\r\n" \
|
||||
"\n" \
|
||||
"\r\n" \
|
||||
"v=0\r\n" \
|
||||
"o=- %s 23 IN IP4 5.6.7.8\r\n" \
|
||||
"c=IN IP4 5.6.7.8\r\n" \
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 4441 RTP/AVP 99\r\n" \
|
||||
"a=rtpmap:99 AMR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define MDCX4_PT2 \
|
||||
"MDCX 18983219 1@mgw MGCP 1.0\r\n" \
|
||||
"M: sendrecv\r" \
|
||||
"M: sendrecv\r\n" \
|
||||
"C: 2\r\n" \
|
||||
"I: %s\r\n" \
|
||||
"L: p:20-20, a:AMR, nt:IN\r\n" \
|
||||
"\n" \
|
||||
"\r\n" \
|
||||
"v=0\r\n" \
|
||||
"o=- %s 23 IN IP4 5.6.7.8\r\n" \
|
||||
"c=IN IP4 5.6.7.8\r\n" \
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 4441 RTP/AVP 99\r\n" \
|
||||
"a=rtpmap:99 AMR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define MDCX4_PT3 \
|
||||
"MDCX 18983220 1@mgw MGCP 1.0\r\n" \
|
||||
"M: sendrecv\r" \
|
||||
"M: sendrecv\r\n" \
|
||||
"C: 2\r\n" \
|
||||
"I: %s\r\n" \
|
||||
"L: a:AMR, nt:IN\r\n" \
|
||||
"\n" \
|
||||
"\r\n" \
|
||||
"v=0\r\n" \
|
||||
"o=- %s 23 IN IP4 5.6.7.8\r\n" \
|
||||
"c=IN IP4 5.6.7.8\r\n" \
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 4441 RTP/AVP 99\r\n" \
|
||||
"a=rtpmap:99 AMR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
/* Test different upper/lower case in options */
|
||||
#define MDCX4_PT4 \
|
||||
"MDCX 18983221 1@mgw MGCP 1.0\r\n" \
|
||||
"m: sendrecv\r" \
|
||||
"m: sendrecv\r\n" \
|
||||
"c: 2\r\n" \
|
||||
"i: %s\r\n" \
|
||||
"l: A:amr, NT:IN\r\n" \
|
||||
"\n" \
|
||||
"\r\n" \
|
||||
"v=0\r\n" \
|
||||
"o=- %s 23 IN IP4 5.6.7.8\r\n" \
|
||||
"c=IN IP4 5.6.7.8\r\n" \
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 4441 RTP/AVP 99\r\n" \
|
||||
"a=rtpmap:99 AMR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define MDCX4_SO \
|
||||
"MDCX 18983222 1@mgw MGCP 1.0\r\n" \
|
||||
"M: sendonly\r" \
|
||||
"M: sendonly\r\n" \
|
||||
"C: 2\r\n" \
|
||||
"I: %s\r\n" \
|
||||
"L: p:20, a:AMR, nt:IN\r\n" \
|
||||
"\n" \
|
||||
"\r\n" \
|
||||
"v=0\r\n" \
|
||||
"o=- %s 23 IN IP4 5.6.7.8\r\n" \
|
||||
"c=IN IP4 5.6.7.8\r\n" \
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 4441 RTP/AVP 99\r\n" \
|
||||
"a=rtpmap:99 AMR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define MDCX4_RO \
|
||||
"MDCX 18983223 1@mgw MGCP 1.0\r\n" \
|
||||
"M: recvonly\r" \
|
||||
"M: recvonly\r\n" \
|
||||
"C: 2\r\n" \
|
||||
"I: %s\r\n" \
|
||||
"L: p:20, a:AMR, nt:IN\r\n"
|
||||
@@ -297,7 +306,7 @@ static void test_strline(void)
|
||||
"c=IN IP4 123.12.12.123\r\n" \
|
||||
"m=audio 5904 RTP/AVP 97\r\n" \
|
||||
"a=rtpmap:97 GSM-EFR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define CRCX_RET \
|
||||
"200 2 OK\r\n" \
|
||||
@@ -310,7 +319,7 @@ static void test_strline(void)
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 16002 RTP/AVP 97\r\n" \
|
||||
"a=rtpmap:97 GSM-EFR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define CRCX_RET_NO_RTPMAP \
|
||||
"200 2 OK\r\n" \
|
||||
@@ -322,7 +331,7 @@ static void test_strline(void)
|
||||
"c=IN IP4 0.0.0.0\r\n" \
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 16002 RTP/AVP 97\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define CRCX_FMTP_RET \
|
||||
"200 2 OK\r\n" \
|
||||
@@ -335,17 +344,17 @@ static void test_strline(void)
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 16006 RTP/AVP 97\r\n" \
|
||||
"a=rtpmap:97 GSM-EFR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define CRCX_ZYN \
|
||||
"CRCX 2 1@mgw MGCP 1.0\r" \
|
||||
"M: recvonly\r" \
|
||||
"CRCX 2 1@mgw MGCP 1.0\r\n" \
|
||||
"M: recvonly\r\n" \
|
||||
"C: 2\r\n" \
|
||||
"\n" \
|
||||
"v=0\r" \
|
||||
"c=IN IP4 123.12.12.123\r" \
|
||||
"m=audio 5904 RTP/AVP 97\r" \
|
||||
"a=rtpmap:97 GSM-EFR/8000\r"
|
||||
"\r\n" \
|
||||
"v=0\r\n" \
|
||||
"c=IN IP4 123.12.12.123\r\n" \
|
||||
"m=audio 5904 RTP/AVP 97\r\n" \
|
||||
"a=rtpmap:97 GSM-EFR/8000\r\n"
|
||||
|
||||
#define CRCX_ZYN_RET \
|
||||
"200 2 OK\r\n" \
|
||||
@@ -371,7 +380,7 @@ static void test_strline(void)
|
||||
"c=IN IP4 123.12.12.123\r\n" \
|
||||
"m=audio 5904 RTP/AVP 97\r\n" \
|
||||
"a=rtpmap:97 GSM-EFR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define CRCX_X_OSMO_IGN_RET \
|
||||
"200 2 OK\r\n" \
|
||||
@@ -384,7 +393,7 @@ static void test_strline(void)
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 16010 RTP/AVP 97\r\n" \
|
||||
"a=rtpmap:97 GSM-EFR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define CRCX_PORT_0 \
|
||||
"CRCX 3 1@mgw MGCP 1.0\r\n" \
|
||||
@@ -396,7 +405,7 @@ static void test_strline(void)
|
||||
"c=IN IP4 123.12.12.123\r\n" \
|
||||
"m=audio 0 RTP/AVP 97\r\n" \
|
||||
"a=rtpmap:97 GSM-EFR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define CRCX_PORT_0_RET \
|
||||
"200 3 OK\r\n" \
|
||||
@@ -409,7 +418,7 @@ static void test_strline(void)
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 16014 RTP/AVP 97\r\n" \
|
||||
"a=rtpmap:97 GSM-EFR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define CRCX_PORT_0_IUFP \
|
||||
"CRCX 4 1@mgw MGCP 1.0\r\n" \
|
||||
@@ -421,7 +430,7 @@ static void test_strline(void)
|
||||
"c=IN IP4 123.12.12.123\r\n" \
|
||||
"m=audio 0 RTP/AVP 96\r\n" \
|
||||
"a=rtpmap:96 VND.3GPP.IUFP/16000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define CRCX_PORT_0_IUFP_RET \
|
||||
"200 4 OK\r\n" \
|
||||
@@ -434,7 +443,45 @@ static void test_strline(void)
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 16016 RTP/AVP 96\r\n" \
|
||||
"a=rtpmap:96 VND.3GPP.IUFP/16000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
/* Do a CRCX in m=sendrecv */
|
||||
#define CRCX_PORT_0_IUFP_SENDRECV \
|
||||
"CRCX 4 1@mgw MGCP 1.0\r\n" \
|
||||
"M: sendrecv\r\n" \
|
||||
"C: 2\r\n" \
|
||||
"L: p:20\r\n" \
|
||||
"\r\n" \
|
||||
"v=0\r\n" \
|
||||
"c=IN IP4 123.12.12.123\r\n" \
|
||||
"m=audio 0 RTP/AVP 96\r\n" \
|
||||
"a=rtpmap:96 VND.3GPP.IUFP/16000\r\n" \
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
/* Do a CRCX using sendrecv mode in the SDP part */
|
||||
#define CRCX_PORT_0_IUFP_SENDRECV2 \
|
||||
"CRCX 4 1@mgw MGCP 1.0\r\n" \
|
||||
"C: 2\r\n" \
|
||||
"L: p:20\r\n" \
|
||||
"\r\n" \
|
||||
"v=0\r\n" \
|
||||
"c=IN IP4 123.12.12.123\r\n" \
|
||||
"a=sendrecv\r\n" \
|
||||
"m=audio 0 RTP/AVP 96\r\n" \
|
||||
"a=rtpmap:96 VND.3GPP.IUFP/16000\r\n" \
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
/* Do a CRCX entirely omitting a mode, i.e. implcit sendrecv */
|
||||
#define CRCX_PORT_0_IUFP_SENDRECV3 \
|
||||
"CRCX 4 1@mgw MGCP 1.0\r\n" \
|
||||
"C: 2\r\n" \
|
||||
"L: p:20\r\n" \
|
||||
"\r\n" \
|
||||
"v=0\r\n" \
|
||||
"c=IN IP4 123.12.12.123\r\n" \
|
||||
"m=audio 0 RTP/AVP 96\r\n" \
|
||||
"a=rtpmap:96 VND.3GPP.IUFP/16000\r\n" \
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define DLCX \
|
||||
"DLCX 7 1@mgw MGCP 1.0\r\n" \
|
||||
@@ -491,7 +538,7 @@ static void test_strline(void)
|
||||
"m=audio 5904 RTP/AVP 18 97\r\n" \
|
||||
"a=rtpmap:18 G729/8000\r\n" \
|
||||
"a=rtpmap:97 GSM-EFR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define CRCX_MULT_2 \
|
||||
"CRCX 2 2@mgw MGCP 1.0\r\n" \
|
||||
@@ -506,7 +553,7 @@ static void test_strline(void)
|
||||
"a=rtpmap:18 G729/8000\r\n" \
|
||||
"a=rtpmap:97 GSM-EFR/8000\r\n" \
|
||||
"a=rtpmap:101 FOO/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define CRCX_MULT_3 \
|
||||
"CRCX 2 3@mgw MGCP 1.0\r\n" \
|
||||
@@ -521,7 +568,7 @@ static void test_strline(void)
|
||||
"a=rtpmap:18 G729/8000\r\n" \
|
||||
"a=rtpmap:97 GSM-EFR/8000\r\n" \
|
||||
"a=rtpmap:101 FOO/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define CRCX_MULT_4 \
|
||||
"CRCX 2 4@mgw MGCP 1.0\r\n" \
|
||||
@@ -536,7 +583,7 @@ static void test_strline(void)
|
||||
"a=rtpmap:18 G729/8000\r\n" \
|
||||
"a=rtpmap:97 GSM-EFR/8000\r\n" \
|
||||
"a=rtpmap:101 FOO/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define CRCX_MULT_GSM_EXACT \
|
||||
"CRCX 259260421 5@mgw MGCP 1.0\r\n" \
|
||||
@@ -600,7 +647,7 @@ static void test_strline(void)
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 16012 RTP/AVP 111\r\n" \
|
||||
"a=rtpmap:111 AMR/8000/1\r\n" \
|
||||
"a=fmtp:111 octet-align=1\r\n" \
|
||||
"a=fmtp:111 mode-change-capability=2; octet-align=1\r\n" \
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define CRCX_NO_LCO_NO_SDP_RET \
|
||||
@@ -625,7 +672,7 @@ static void test_strline(void)
|
||||
"c=IN IP4 123.12.12.123\r\n" \
|
||||
"m=audio 5904 RTP/AVP 97\r\n" \
|
||||
"a=rtpmap:97 GSM-EFR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define CRCX_NULL_RET "502 2 FAIL\r\n"
|
||||
|
||||
@@ -675,6 +722,9 @@ static const struct mgcp_test tests[] = {
|
||||
{"RQNT_NULL", RQNT_NULL, RQNT_NULL_RET},
|
||||
{"CRCX_PORT_0", CRCX_PORT_0, CRCX_PORT_0_RET, 97},
|
||||
{"CRCX_PORT_0_IUFP", CRCX_PORT_0_IUFP, CRCX_PORT_0_IUFP_RET, 96},
|
||||
{"CRCX_PORT_0_IUFP_SENDRECV", CRCX_PORT_0_IUFP_SENDRECV, CRCX_PORT_0_IUFP_RET, 96},
|
||||
{"CRCX_PORT_0_IUFP_SENDRECV2", CRCX_PORT_0_IUFP_SENDRECV2, CRCX_PORT_0_IUFP_RET, 96},
|
||||
{"CRCX_PORT_0_IUFP_SENDRECV3", CRCX_PORT_0_IUFP_SENDRECV3, CRCX_PORT_0_IUFP_RET, 96},
|
||||
};
|
||||
|
||||
static const struct mgcp_test retransmit[] = {
|
||||
@@ -893,6 +943,7 @@ static void test_messages(void)
|
||||
}
|
||||
} else if (check_response(msg->data, t->exp_resp) != 0) {
|
||||
printf("%s failed.\n", t->name);
|
||||
fflush(stdout);
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ v=0
|
||||
c=IN IP4 123.12.12.123
|
||||
m=audio 5904 RTP/AVP 97
|
||||
a=rtpmap:97 GSM-EFR/8000
|
||||
a=ptime:40
|
||||
a=ptime:20
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
@@ -101,40 +101,42 @@ Testing MDCX4_ADDR000
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
MDCX 18983216 1@mgw MGCP 1.0
|
||||
M: sendrecv
|
||||
M: sendrecv
|
||||
C: 2
|
||||
I: %s
|
||||
L: p:20, a:AMR, nt:IN
|
||||
L: p:20, a:AMR, nt:IN
|
||||
|
||||
v=0
|
||||
o=- %s 23 IN IP4 0.0.0.0
|
||||
c=IN IP4 0.0.0.0
|
||||
t=0 0
|
||||
m=audio 4441 RTP/AVP 99
|
||||
a=rtpmap:99 AMR/8000
|
||||
a=rtpmap:99 AMR/8000
|
||||
a=ptime:20
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
checking response:
|
||||
using message with patched conn_id for comparison
|
||||
Response matches our expectations.
|
||||
Response matches our expectations.
|
||||
(response contains a connection id)
|
||||
|
||||
================================================
|
||||
Testing MDCX4
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
MDCX 18983217 1@mgw MGCP 1.0
|
||||
MDCX 18983217 1@mgw MGCP 1.0
|
||||
M: sendrecv
|
||||
C: 2
|
||||
I: %s
|
||||
L: p:20, a:AMR, nt:IN
|
||||
I: %s
|
||||
|
||||
v=0
|
||||
o=- %s 23 IN IP4 5.6.7.8
|
||||
c=IN IP4 5.6.7.8
|
||||
t=0 0
|
||||
m=audio 4441 RTP/AVP 99
|
||||
a=rtpmap:99 AMR/8000
|
||||
m=audio 4441 RTP/AVP 99
|
||||
a=ptime:20
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
@@ -148,17 +150,18 @@ Testing MDCX4_PT1
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
MDCX 18983218 1@mgw MGCP 1.0
|
||||
---------8<---------
|
||||
M: SENDRECV
|
||||
C: 2
|
||||
I: %s
|
||||
L: p:20-40, a:AMR, nt:IN
|
||||
C: 2
|
||||
|
||||
v=0
|
||||
o=- %s 23 IN IP4 5.6.7.8
|
||||
c=IN IP4 5.6.7.8
|
||||
t=0 0
|
||||
m=audio 4441 RTP/AVP 99
|
||||
a=rtpmap:99 AMR/8000
|
||||
t=0 0
|
||||
a=ptime:20
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
@@ -172,17 +175,18 @@ Testing MDCX4_PT2
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
MDCX 18983219 1@mgw MGCP 1.0
|
||||
creating message from statically defined input:
|
||||
M: sendrecv
|
||||
C: 2
|
||||
I: %s
|
||||
L: p:20-20, a:AMR, nt:IN
|
||||
M: sendrecv
|
||||
|
||||
v=0
|
||||
o=- %s 23 IN IP4 5.6.7.8
|
||||
c=IN IP4 5.6.7.8
|
||||
t=0 0
|
||||
m=audio 4441 RTP/AVP 99
|
||||
a=rtpmap:99 AMR/8000
|
||||
c=IN IP4 5.6.7.8
|
||||
a=ptime:20
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
@@ -196,17 +200,18 @@ Testing MDCX4_PT3
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
MDCX 18983220 1@mgw MGCP 1.0
|
||||
Testing MDCX4_PT3
|
||||
M: sendrecv
|
||||
C: 2
|
||||
I: %s
|
||||
L: a:AMR, nt:IN
|
||||
MDCX 18983220 1@mgw MGCP 1.0
|
||||
|
||||
v=0
|
||||
o=- %s 23 IN IP4 5.6.7.8
|
||||
c=IN IP4 5.6.7.8
|
||||
t=0 0
|
||||
m=audio 4441 RTP/AVP 99
|
||||
a=rtpmap:99 AMR/8000
|
||||
o=- %s 23 IN IP4 5.6.7.8
|
||||
a=ptime:20
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
@@ -220,17 +225,18 @@ Testing MDCX4_PT4
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
MDCX 18983221 1@mgw MGCP 1.0
|
||||
================================================
|
||||
m: sendrecv
|
||||
c: 2
|
||||
i: %s
|
||||
l: A:amr, NT:IN
|
||||
---------8<---------
|
||||
|
||||
v=0
|
||||
o=- %s 23 IN IP4 5.6.7.8
|
||||
c=IN IP4 5.6.7.8
|
||||
t=0 0
|
||||
m=audio 4441 RTP/AVP 99
|
||||
a=rtpmap:99 AMR/8000
|
||||
v=0
|
||||
a=ptime:20
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
@@ -244,17 +250,18 @@ Testing MDCX4_SO
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
MDCX 18983222 1@mgw MGCP 1.0
|
||||
|
||||
M: sendonly
|
||||
C: 2
|
||||
I: %s
|
||||
L: p:20, a:AMR, nt:IN
|
||||
creating message from statically defined input:
|
||||
|
||||
v=0
|
||||
o=- %s 23 IN IP4 5.6.7.8
|
||||
c=IN IP4 5.6.7.8
|
||||
t=0 0
|
||||
m=audio 4441 RTP/AVP 99
|
||||
a=rtpmap:99 AMR/8000
|
||||
|
||||
a=ptime:20
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
@@ -267,7 +274,8 @@ Testing MDCX4_RO
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
MDCX 18983223 1@mgw MGCP 1.0
|
||||
(response contains a connection id)
|
||||
M: recvonly
|
||||
C: 2
|
||||
I: %s
|
||||
L: p:20, a:AMR, nt:IN
|
||||
|
||||
@@ -296,9 +304,15 @@ Response matches our expectations.
|
||||
Testing CRCX_ZYN
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
using message as statically defined for comparison
|
||||
CRCX 2 1@mgw MGCP 1.0
|
||||
M: recvonly
|
||||
C: 2
|
||||
|
||||
v=0
|
||||
c=IN IP4 123.12.12.123
|
||||
m=audio 5904 RTP/AVP 97
|
||||
a=rtpmap:97 GSM-EFR/8000
|
||||
|
||||
(response does not contain a connection id)
|
||||
---------8<---------
|
||||
checking response:
|
||||
using message with patched conn_id for comparison
|
||||
@@ -414,7 +428,7 @@ v=0
|
||||
c=IN IP4 123.12.12.123
|
||||
m=audio 5904 RTP/AVP 97
|
||||
a=rtpmap:97 GSM-EFR/8000
|
||||
================================================
|
||||
a=ptime:20
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
@@ -479,7 +493,7 @@ v=0
|
||||
c=IN IP4 123.12.12.123
|
||||
m=audio 5904 RTP/AVP 97
|
||||
a=rtpmap:97 GSM-EFR/8000
|
||||
Testing CRCX
|
||||
a=ptime:20
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
@@ -550,7 +564,7 @@ v=0
|
||||
c=IN IP4 123.12.12.123
|
||||
m=audio 5904 RTP/AVP 97
|
||||
a=rtpmap:97 GSM-EFR/8000
|
||||
================================================
|
||||
a=ptime:20
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
@@ -612,7 +626,7 @@ v=0
|
||||
c=IN IP4 123.12.12.123
|
||||
m=audio 0 RTP/AVP 97
|
||||
a=rtpmap:97 GSM-EFR/8000
|
||||
================================================
|
||||
a=ptime:20
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
@@ -633,7 +647,69 @@ v=0
|
||||
c=IN IP4 123.12.12.123
|
||||
m=audio 0 RTP/AVP 96
|
||||
a=rtpmap:96 VND.3GPP.IUFP/16000
|
||||
================================================
|
||||
a=ptime:20
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
using message with patched conn_id for comparison
|
||||
Response matches our expectations.
|
||||
(response contains a connection id)
|
||||
|
||||
================================================
|
||||
Testing CRCX_PORT_0_IUFP_SENDRECV
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
CRCX 4 1@mgw MGCP 1.0
|
||||
M: sendrecv
|
||||
C: 2
|
||||
L: p:20
|
||||
|
||||
v=0
|
||||
c=IN IP4 123.12.12.123
|
||||
m=audio 0 RTP/AVP 96
|
||||
a=rtpmap:96 VND.3GPP.IUFP/16000
|
||||
a=ptime:20
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
using message with patched conn_id for comparison
|
||||
Response matches our expectations.
|
||||
(response contains a connection id)
|
||||
|
||||
================================================
|
||||
Testing CRCX_PORT_0_IUFP_SENDRECV2
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
CRCX 4 1@mgw MGCP 1.0
|
||||
C: 2
|
||||
L: p:20
|
||||
|
||||
v=0
|
||||
c=IN IP4 123.12.12.123
|
||||
a=sendrecv
|
||||
m=audio 0 RTP/AVP 96
|
||||
a=rtpmap:96 VND.3GPP.IUFP/16000
|
||||
a=ptime:20
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
using message with patched conn_id for comparison
|
||||
Response matches our expectations.
|
||||
(response contains a connection id)
|
||||
|
||||
================================================
|
||||
Testing CRCX_PORT_0_IUFP_SENDRECV3
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
CRCX 4 1@mgw MGCP 1.0
|
||||
C: 2
|
||||
L: p:20
|
||||
|
||||
v=0
|
||||
c=IN IP4 123.12.12.123
|
||||
m=audio 0 RTP/AVP 96
|
||||
a=rtpmap:96 VND.3GPP.IUFP/16000
|
||||
a=ptime:20
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
@@ -654,7 +730,7 @@ v=0
|
||||
c=IN IP4 123.12.12.123
|
||||
m=audio 5904 RTP/AVP 97
|
||||
a=rtpmap:97 GSM-EFR/8000
|
||||
================================================
|
||||
a=ptime:20
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
@@ -672,7 +748,7 @@ v=0
|
||||
c=IN IP4 123.12.12.123
|
||||
m=audio 5904 RTP/AVP 97
|
||||
a=rtpmap:97 GSM-EFR/8000
|
||||
Response matches our expectations.
|
||||
a=ptime:20
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
@@ -784,7 +860,7 @@ v=0
|
||||
c=IN IP4 123.12.12.123
|
||||
m=audio 5904 RTP/AVP 97
|
||||
a=rtpmap:97 GSM-EFR/8000
|
||||
Response matches our expectations.
|
||||
a=ptime:20
|
||||
|
||||
---------8<---------
|
||||
creating message from statically defined input:
|
||||
@@ -1238,7 +1314,7 @@ c=IN IP4 123.12.12.123
|
||||
m=audio 5904 RTP/AVP 18 97
|
||||
a=rtpmap:18 G729/8000
|
||||
a=rtpmap:97 GSM-EFR/8000
|
||||
creating message from statically defined input:
|
||||
a=ptime:20
|
||||
|
||||
---------8<---------
|
||||
creating message from statically defined input:
|
||||
@@ -1255,7 +1331,7 @@ m=audio 5904 RTP/AVP 18 97 101
|
||||
a=rtpmap:18 G729/8000
|
||||
a=rtpmap:97 GSM-EFR/8000
|
||||
a=rtpmap:101 FOO/8000
|
||||
---------8<---------
|
||||
a=ptime:20
|
||||
|
||||
---------8<---------
|
||||
creating message from statically defined input:
|
||||
@@ -1272,7 +1348,7 @@ m=audio 5904 RTP/AVP
|
||||
a=rtpmap:18 G729/8000
|
||||
a=rtpmap:97 GSM-EFR/8000
|
||||
a=rtpmap:101 FOO/8000
|
||||
---------8<---------
|
||||
a=ptime:20
|
||||
|
||||
---------8<---------
|
||||
creating message from statically defined input:
|
||||
@@ -1289,7 +1365,7 @@ m=audio 5904 RTP/AVP 18
|
||||
a=rtpmap:18 G729/8000
|
||||
a=rtpmap:97 GSM-EFR/8000
|
||||
a=rtpmap:101 FOO/8000
|
||||
---------8<---------
|
||||
a=ptime:20
|
||||
|
||||
---------8<---------
|
||||
creating message from statically defined input:
|
||||
@@ -1371,7 +1447,7 @@ v=0
|
||||
c=IN IP4 123.12.12.123
|
||||
m=audio 5904 RTP/AVP 97
|
||||
a=rtpmap:97 GSM-EFR/8000
|
||||
Testing no sequence flow on initial packet
|
||||
a=ptime:20
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
|
||||
62
tests/sdp/Makefile.am
Normal file
62
tests/sdp/Makefile.am
Normal file
@@ -0,0 +1,62 @@
|
||||
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 = \
|
||||
$(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
|
||||
764
tests/sdp/sdp_msg_test.c
Normal file
764
tests/sdp/sdp_msg_test.c
Normal file
@@ -0,0 +1,764 @@
|
||||
#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_err err;
|
||||
|
||||
printf("\n[%d]\n", i);
|
||||
dump_sdp(t->sdp_input, "sdp input: ");
|
||||
|
||||
sdp_msg = osmo_sdp_msg_decode(ctx, t->sdp_input, &err);
|
||||
|
||||
if (err.rc) {
|
||||
printf("ERROR: %s at %s\n", strerror(abs(err.rc)),
|
||||
osmo_quote_cstr_c(print_ctx, err.at_input_str, err.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[] = {
|
||||
{
|
||||
"identical codecs lead to no change"
|
||||
,
|
||||
SDP_1
|
||||
,
|
||||
"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"
|
||||
,
|
||||
SDP_1
|
||||
},
|
||||
{
|
||||
"identical codecs in different order also lead to no change"
|
||||
,
|
||||
SDP_1
|
||||
,
|
||||
"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"
|
||||
,
|
||||
SDP_1
|
||||
},
|
||||
{
|
||||
"identical codecs with mismatching payload type numbers also lead to no change"
|
||||
,
|
||||
SDP_1
|
||||
,
|
||||
"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"
|
||||
,
|
||||
SDP_1
|
||||
},
|
||||
{
|
||||
"identical codecs plus some extra codecs also lead to no change"
|
||||
,
|
||||
SDP_1
|
||||
,
|
||||
"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"
|
||||
,
|
||||
SDP_1
|
||||
},
|
||||
{
|
||||
"some codecs removed",
|
||||
SDP_1,
|
||||
SDP_2,
|
||||
SDP_2,
|
||||
},
|
||||
{
|
||||
"other codecs removed",
|
||||
SDP_1,
|
||||
SDP_3,
|
||||
SDP_4,
|
||||
},
|
||||
{
|
||||
"all codecs removed",
|
||||
SDP_1
|
||||
,
|
||||
"s=empty"
|
||||
,
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"some real world test case",
|
||||
SDP_5, SDP_5, 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, NULL);
|
||||
if (!sdp_msg_a) {
|
||||
printf("ERROR parsing SDP A\n");
|
||||
break;
|
||||
}
|
||||
dump_sdp(sdp_msg_logstr(sdp_msg_a), "parsed SDP A: ");
|
||||
|
||||
sdp_msg_b = osmo_sdp_msg_decode(ctx, t->sdp_msg_b, NULL);
|
||||
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, 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
1316
tests/sdp/sdp_msg_test.ok
Normal file
1316
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