Compare commits

...

20 Commits

Author SHA1 Message Date
Neels Hofmeyr
e5ac26212a sdp strs fu
Change-Id: Id8ab7f870067d793a2b8934f2cca51cf06ee5dc2
2024-06-24 03:02:22 +02:00
Neels Hofmeyr
b86c401cf9 wip
Change-Id: I4e3fbcf85f3e1867aeb577681c7e32a01e56f2c4
2024-06-24 03:02:22 +02:00
Neels Hofmeyr
ab70b7f3fa add libosmo-sdp: add sdp_msg.h,.c
Change-Id: Id53c2c6eea3726f63a1399ae985f8aa3344e32c8
2024-06-24 03:02:22 +02:00
Neels Hofmeyr
8916c4fe39 codec list is empty
Change-Id: I5e80f5737f22dc81ac16448fc59937330d24432e
2024-06-24 03:02:22 +02:00
Neels Hofmeyr
094c3875f3 add libosmo-sdp: add codec_list_by_payload_type
Change-Id: I7a09b0e669d787a1ac0ec7ed7541c107fd0f32db
2024-06-24 03:02:22 +02:00
Neels Hofmeyr
de8b171eda add libosmo-sdp: add codec_list_intersection
Change-Id: Ie57e4384854a1152dccf9577fdb5f046a1f17e0f
2024-06-24 03:02:22 +02:00
Neels Hofmeyr
900d0c40fe add libosmo-sdp: codec_list_cmp()
Change-Id: I4a2087c3c03ef11df03c00a14953b0d0b6f977f3
2024-06-24 03:02:22 +02:00
Neels Hofmeyr
e5bb708f38 add libosmo-sdp: codec_list_first()
Change-Id: I43653820d8b4d07bf5d7ed25b6172fec26eb2cdf
2024-06-24 03:02:22 +02:00
Neels Hofmeyr
158b4dfacc add libosmo-sdp: codec_list_to_str()
Change-Id: I8a421ff8280db3f1ec399832f1a86c515a0888af
2024-06-24 03:02:22 +02:00
Neels Hofmeyr
4782f8fa1d add libosmo-sdp: osmo_sdp_codec_list.h,c, first part
Change-Id: If170566c666c4f4010091bc90912b13a12f77de8
2024-06-24 03:02:22 +02:00
Neels Hofmeyr
646126d0f9 add libosmo-sdp: sdp_codec.h,c
The primary user is osmo-msc. This API's predecessor currently lives in
osmo-msc.git and is now being moved here, in a matured form.

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

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

Change-Id: I2cec2667796dae91441ad9357d11854239880aea
2024-06-24 03:02:22 +02:00
Neels Hofmeyr
e735cdd319 add libosmo-sdp: fmtp_amr_match()
Change-Id: I0fb77c022c4fb195ba45631dda9cbd2215c02510
2024-06-24 03:02:22 +02:00
Neels Hofmeyr
5c1e7e2f13 mgcp-client: MGCP response: pass fmtp to caller
When receiving MGCP responses, so far libosmo-mgcp-client completely
ignored a=fmtp: parameters (like 'octet-align'). Add fmtp parsing to
pass the fmtp string to the caller as-is.

Since the responses so far never included the octet_aligned flags, do
not bother to parse fmtp to populate the legacy items. New callers
should use the fmtp string.

Change-Id: If8ca5c3880cad9e41b80e9d1c821439b0d7b7e23
2024-06-24 03:02:22 +02:00
Neels Hofmeyr
794235e53d mgw, client: use new fmtp.h in ptmap: allow all fmtp
Remove the limit of having only one AMR octet-aligned fmtp parameter per
MGCP message. Instead allow any arbitrary fmtp options, one per every
codec.

Deprecate all use of struct mgcp_codec_param. Instead, store and pass
plain fmtp strings.

Provide legacy shims that still act correctly for any callers that may
pass the old struct mgcp_codec_param. (I'm not sure if we need to keep
this, but we can always drop it in another patch.)

Adjust one mgcp_test.c: instead of returning only the octet-aligned
parameter, now osmo-mgw keeps and returns all the fmtp parameters that
the user provided. So add the missing "mode-change-capability".

Related: OS#6171
Related: osmo-msc Ief9225c9bcf7525a9a0a07c282ffb8cc0d092186
Change-Id: If58590bda8627519ff07e0b6f43aa47a274f052b
2024-06-24 03:02:22 +02:00
Neels Hofmeyr
b5618dade5 add libosmo-sdp [1/n] add fmtp.h
Add new noinst-library libosmo-sdp.la.

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

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

In this patch: parse and compose SDP fmtp strings.

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

Change-Id: I6128852f4d249e90319f43d6cd6ed0a9a2ed0430
2024-06-24 03:02:22 +02:00
Neels Hofmeyr
f84c0e681e make libosmo-mgcp.a an .la lib
.la libs aka LTLIB can include library dependencies.

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

Change-Id: I5302d56e571d0da9061c275ce13d4f085c044671
2024-06-24 03:02:22 +02:00
Neels Hofmeyr
9f23b7b5d5 do not FAIL on CRCX in sendrecv mode
Currently, a CRCX in sendrecv mode results in:

  DLMGCP ERROR endpoint:rtpbridge/2@mgw CI:7F4C8EDD CRCX: selected connection mode type requires an opposite end! (mgcp_protocol.c:1090)

But it is not actually practical, nor logical to require another conn
for the 'sendrecv' ConnectionMode.

Impractical: If I want to create two conns on an endpoint that both are
in 'sendrecv' mode, I have to send two CRCX, one for each conn. At the
time of the first CRCX, there cannot be any other conn, so I am
currently forced to send a different ConnectionMode in the CRCX,
followed by another MDCX later, just to change the first conn to
sendrecv.

Illogical: In a situation where two conns are currently in sendrecv
mode, if I now DLCX one of them, I can legally reach a state with a
single conn in sendrecv mode, just as currently forbidden for CRCX.

In general, MGCP is not forcing any particular number of connections.

Simply start forwarding RTP as soon as there is a remote conn, and not
require another explicit MDCX.

Related: SYS#6974 SYS#6907
Related: osmo-ttcn3-hacks I00fd854f058f7f53e2f579e8481ca2b9253f08e3
Change-Id: Ic089485543c5c97a35c7ae24fe0f622bf57d1976
2024-06-24 02:48:54 +02:00
Neels Hofmeyr
01d2b902ad mgcp_test: add CRCX for IUFP in sendrecv
Note, there are also ttcn3 tests sending the same to osmo-mgw being
submitted along with these patches, and the ttcn3 tests currently fail:
osmo-mgw rejects the connection mode "sendrecv" in the first CRCX.

Change-Id: Icdb1085b8e44ac4cff6c457163349b81e81bf765
2024-06-24 02:23:46 +02:00
Neels Hofmeyr
a263aeebc1 mgcp_test: test a=ptime:20, not 40
In our GSM world, the packet time is 20 ms, hence 20 is the typical
value that we should test. Maybe we can do one test with 40 at most, but
we'll most probably never see anything else than 20 in practice.

Change-Id: Ia2292198bf6e3b72912afd69607654ca77fd549d
2024-06-24 01:56:24 +02:00
Neels Hofmeyr
3a44c464f5 mgcp_test.c: fix various missing '\r' and '\n'
The only valid line endings are '\n' and '\r\n'.
We usually use '\r\n' like we were in MSDOS.

MGCP, RFC3435 3.1:

   Headers and session descriptions are encoded as a set of text lines,
   separated by a carriage return and line feed character (or,
   optionally, a single line-feed character).

SDP, RFC8866 5:

   The sequence CRLF (0x0d0a) is used to end a line,
   although parsers SHOULD be tolerant and also accept lines terminated
   with a single newline character.

There should probably be tests for '\n' line endings, but mixing them in
the same MGCP message is ridiculous.

Change-Id: I6d530535a3a5f1d1a0716ab9e4a8079ba1de242e
2024-06-24 01:43:34 +02:00
45 changed files with 5629 additions and 228 deletions

View File

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

View File

@@ -8,6 +8,11 @@ nobase_include_HEADERS = \
osmocom/mgcp_client/mgcp_client_endpoint_fsm.h \
osmocom/mgcp_client/mgcp_client_fsm.h \
osmocom/mgcp_client/mgcp_client_pool.h \
osmocom/sdp/fmtp.h \
osmocom/sdp/sdp_codec.h \
osmocom/sdp/sdp_codec_list.h \
osmocom/sdp/sdp_msg.h \
osmocom/sdp/sdp_strings.h \
$(NULL)
noinst_HEADERS = \

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,34 @@
/* Public API for codec management in SDP messages: managing SDP fmtp strings. */
/*
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved.
*
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
bool osmo_sdp_fmtp_get_val(char *val, size_t val_size, const char *fmtp, const char *option_name);
int64_t osmo_sdp_fmtp_get_int(const char *fmtp, const char *option_name, int64_t default_value);
bool osmo_sdp_fmtp_amr_is_octet_aligned(const char *fmtp);
bool osmo_sdp_fmtp_amr_match(const char *a, const char *b);

View File

@@ -0,0 +1,126 @@
/* Public API for codec management in SDP messages. */
/*
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved.
*
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/talloc.h>
/* RFC-8866 5.14 and 6.6.
*
* Represent the items describing an SDP codec entry, as in:
*
* m=audio 1234 RTP/AVP <payload_type>
* a=rtpmap:<payload_type> <encoding-name>/<clock-rate>
* a=fmtp:<payload_type> <fmtp>
*
* For example:
*
* m=audio 1234 RTP/AVP 98
* a=rtpmap:98 AMR/8000
* a=fmtp:98 octet-align=1;mode-set=0,2,4,7
*/
struct osmo_sdp_codec {
/* Payload type number ("payload-type"), like 3 for GSM-FR. Limited to 0..127. */
int8_t payload_type;
/* Encoding name like "GSM", "AMR", "GSM-EFR".
*
* RFC-8866 defines no length limit on the encoding name. This API leaves it up to the caller to provide
* sufficient space, via the SDP_SIZES_* definitions.
*
* encoding-name = token
* token = 1*(token-char)
* token-char = ALPHA / DIGIT
* / "!" / "#" / "$" / "%" / "&"
* / "'" ; (single quote)
* / "*" / "+" / "-" / "." / "^" / "_"
* / "`" ; (Grave accent)
*/
char *encoding_name;
/* Samplerate ("clock-rate"), usually 8000 for GSM. */
unsigned int rate;
/* Codec parameters as supplied in SDP line 'a=fmtp:<payload-type> <format-specific-params>'. This holds only
* the 'format-specific-params' bytestring. For example, for SDP line 'a=fmtp:123 param1=val1;param2=val2', this
* holds only the , "param1=val1;param2=val2" part. For the buffer size, see fmtp_size. */
char *fmtp;
/* Entry used by osmo_sdp_codec_list. */
struct llist_head entry;
/* For future extension, always set to false. */
bool v2;
};
struct osmo_sdp_codec *osmo_sdp_codec_alloc(void *ctx);
int osmo_sdp_codec_set(struct osmo_sdp_codec *c,
int8_t payload_type, const char *encoding_name, unsigned int rate, const char *fmtp);
int osmo_sdp_codec_set_encoding_name(struct osmo_sdp_codec *c, const char *encoding_name);
int osmo_sdp_codec_set_fmtp(struct osmo_sdp_codec *c, const char *fmtp);
bool osmo_sdp_codec_is_set(const struct osmo_sdp_codec *a);
int osmo_sdp_codec_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_codec *codec);
char *osmo_sdp_codec_to_str_c(void *ctx, const struct osmo_sdp_codec *codec);
int osmo_sdp_codec_from_str(struct osmo_sdp_codec *dst, const char *str, int str_len);
enum osmo_sdp_cmp {
OSMO_SDP_CMP_IGNORE = 0,
OSMO_SDP_CMP_EQUIVALENT,
OSMO_SDP_CMP_EXACT,
};
/*! Indicate how to match SDP codecs to various osmo_sdp_*() functions.
* Callers may define own flags, or use predefined instances:
* osmo_sdp_codec_cmp_exact, osmo_sdp_codec_cmp_equivalent, ...
*
* For example, to trigger some action if any item has changed, set all items to true / OSMO_SDP_CMP_EXACT (see
* osmo_sdp_codec_cmp_exact).
* To find codecs that are the same between two SDP sessions, set payload_type=false and fmtp=OSMO_SDP_CMP_EQUIVALENT
* (see osmo_sdp_codec_cmp_equivalent).
* To just list all contained "AMR" codecs, set only encoding_name=true (see osmo_sdp_codec_cmp_name).
*/
struct osmo_sdp_codec_cmp_flags {
/*! true = compare payload type numbers 1:1; false = ignore. */
bool payload_type;
/*! true = compare encoding_name 1:1; false = ignore. */
bool encoding_name;
/*! true = compare rate 1:1; false = ignore. */
bool rate;
/*! OSMO_SDP_CMP_IGNORE = ignore fmtp;
* OSMO_SDP_CMP_EQUIVALENT = use osmo_sdp_fmtp_amr_match() for AMR, otherwise compare 1:1;
* OSMO_SDP_CMP_EXACT = compare 1:1.
*/
enum osmo_sdp_cmp fmtp;
};
extern const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_exact;
extern const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_equivalent;
extern const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_name;
int osmo_sdp_codec_cmp(const struct osmo_sdp_codec *a, const struct osmo_sdp_codec *b,
const struct osmo_sdp_codec_cmp_flags *cmp);

View File

@@ -0,0 +1,69 @@
/* Public API for codec management in SDP messages: list of struct osmo_sdp_codec. */
/*
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved.
*
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <osmocom/sdp/sdp_codec.h>
struct osmo_sdp_codec_list {
struct llist_head list;
/* For future extension, always set to false. */
bool v2;
};
struct osmo_sdp_codec_list *osmo_sdp_codec_list_alloc(void *ctx);
void osmo_sdp_codec_list_free_items(struct osmo_sdp_codec_list *codec_list);
int8_t osmo_sdp_codec_list_get_unused_dyn_pt_nr(const struct osmo_sdp_codec_list *codec_list, int8_t suggest_pt_nr);
struct osmo_sdp_codec *osmo_sdp_codec_list_add_empty(struct osmo_sdp_codec_list *codec_list);
struct osmo_sdp_codec *osmo_sdp_codec_list_add(struct osmo_sdp_codec_list *codec_list,
const struct osmo_sdp_codec *codec,
const struct osmo_sdp_codec_cmp_flags *once, bool pick_unused_pt_nr);
int osmo_sdp_codec_list_remove(struct osmo_sdp_codec_list *codec_list, const struct osmo_sdp_codec *codec,
const struct osmo_sdp_codec_cmp_flags *cmpf);
void osmo_sdp_codec_list_remove_entry(struct osmo_sdp_codec *codec);
int osmo_sdp_codec_list_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_codec_list *codec_list, bool summarize);
char *osmo_sdp_codec_list_to_str_c(void *ctx, const struct osmo_sdp_codec_list *codec_list, bool summarize);
struct osmo_sdp_codec *osmo_sdp_codec_list_first(const struct osmo_sdp_codec_list *list);
int osmo_sdp_codec_list_move_to_first(struct osmo_sdp_codec_list *codec_list, const struct osmo_sdp_codec *codec,
const struct osmo_sdp_codec_cmp_flags *cmpf);
#define osmo_sdp_codec_list_foreach(STRUCT_SDP_CODEC_P, SDP_CODEC_LIST) \
llist_for_each_entry(STRUCT_SDP_CODEC_P, &(SDP_CODEC_LIST)->list, entry)
#define osmo_sdp_codec_list_foreach_safe(STRUCT_SDP_CODEC_P, SAFE_P, SDP_CODEC_LIST) \
llist_for_each_entry_safe(STRUCT_SDP_CODEC_P, SAFE_P, &(SDP_CODEC_LIST)->list, entry)
int osmo_sdp_codec_list_cmp(const struct osmo_sdp_codec_list *a, const struct osmo_sdp_codec_list *b,
const struct osmo_sdp_codec_cmp_flags *cmpf);
void osmo_sdp_codec_list_intersection(struct osmo_sdp_codec_list *dst, const struct osmo_sdp_codec_list *other,
const struct osmo_sdp_codec_cmp_flags *cmpf,
bool translate_payload_type_numbers);
struct osmo_sdp_codec *osmo_sdp_codec_list_by_payload_type(struct osmo_sdp_codec_list *codec_list, int8_t payload_type);
bool osmo_sdp_codec_list_is_empty(const struct osmo_sdp_codec_list *codec_list);

View File

@@ -0,0 +1,11 @@
/* Internal header for non-public API shared across .c files */
#pragma once
struct token {
const char *start;
const char *end;
};
/* Copy a string from [start,end[, return as talloc allocated under ctx in *dst.
* If *dst is non-NULL, talloc_free(*dst) first. */
void token_copy(void *ctx, char **dst, const struct token *t);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -19,15 +19,15 @@ AM_LDFLAGS = \
$(COVERAGE_LDFLAGS) \
$(NULL)
noinst_LIBRARIES = \
libosmo-mgcp.a \
noinst_LTLIBRARIES = \
libosmo-mgcp.la \
$(NULL)
noinst_HEADERS = \
g711common.h \
$(NULL)
libosmo_mgcp_a_SOURCES = \
libosmo_mgcp_la_SOURCES = \
mgcp_protocol.c \
mgcp_network.c \
mgcp_vty.c \
@@ -43,3 +43,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)

View File

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

View File

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

View File

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

View File

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

View 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
View File

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

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

@@ -0,0 +1,251 @@
/* Codec management in SDP messages. */
/*
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved.
*
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <ctype.h>
#include <osmocom/core/utils.h>
#include <osmocom/sdp/fmtp.h>
#include <osmocom/sdp/sdp_codec.h>
#include <osmocom/sdp/sdp_internal.h>
struct osmo_sdp_codec *osmo_sdp_codec_alloc(void *ctx)
{
return talloc_zero(ctx, struct osmo_sdp_codec);
}
int osmo_sdp_codec_set(struct osmo_sdp_codec *c,
int8_t payload_type, const char *encoding_name, unsigned int rate, const char *fmtp)
{
c->rate = rate;
osmo_sdp_codec_set_encoding_name(c, encoding_name);
osmo_sdp_codec_set_fmtp(c, fmtp);
c->payload_type = payload_type;
return 0;
}
int osmo_sdp_codec_set_encoding_name(struct osmo_sdp_codec *c, const char *encoding_name)
{
osmo_talloc_replace_string(c, &c->encoding_name, encoding_name);
return 0;
}
int osmo_sdp_codec_set_fmtp(struct osmo_sdp_codec *c, const char *fmtp)
{
osmo_talloc_replace_string(c, &c->fmtp, fmtp);
return 0;
}
bool osmo_sdp_codec_is_set(const struct osmo_sdp_codec *a)
{
return a && a->encoding_name && a->encoding_name[0];
}
int osmo_sdp_codec_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_codec *codec)
{
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
if (!codec) {
OSMO_STRBUF_PRINTF(sb, "NULL");
return sb.chars_needed;
}
if (codec->encoding_name && codec->encoding_name[0])
OSMO_STRBUF_PRINTF(sb, "%s", codec->encoding_name);
if (codec->rate != 8000)
OSMO_STRBUF_PRINTF(sb, "/%u", codec->rate);
if (codec->fmtp && codec->fmtp[0])
OSMO_STRBUF_PRINTF(sb, ":%s", codec->fmtp);
OSMO_STRBUF_PRINTF(sb, "#%d", codec->payload_type);
return sb.chars_needed;
}
char *osmo_sdp_codec_to_str_c(void *ctx, const struct osmo_sdp_codec *codec)
{
OSMO_NAME_C_IMPL(ctx, 32, "osmo_sdp_codec_to_str_c-ERROR", osmo_sdp_codec_to_str_buf, codec)
}
/*! Parse a codec string as from osmo_sdp_codec_to_str_buf() back to an osmo_sdp_codec struct.
* Write the parsed result to *dst, using ctx as talloc parent.
* The input string is like <encoding_name>[:<fmtp-string>][#<payload-type-nr>]
* for example:
* "FOO:my-fmtp=1;my-other-fmtp=2#42"
* Note that ';' are separators only within the fmtp string. This function does not separate those. In above example,
* the fmtp string part is "my-fmtp=val;my-other-fmtp=val2" and ends up in dst->ftmp as-is.
* Parse at most str_len characters, or the entire string when str_len < 0 or str_len > strlen(str).
* Return 0 on success, negative on failure. */
int osmo_sdp_codec_from_str(struct osmo_sdp_codec *dst, const char *str, int str_len)
{
const char *pos = str;
const char *str_end = str + (str_len >= 0 ? str_len : strlen(str));
const char *p2;
struct token token_encoding_name = {};
struct token token_rate = {};
struct token token_fmtp = {};
struct token token_payload_type = {};
struct token *new_t = NULL;
/* start with the encoding name */
struct token *t = &token_encoding_name;
t->start = pos;
for (; pos < str_end; pos++) {
new_t = NULL;
switch (*pos) {
case '/':
new_t = &token_rate;
break;
case ':':
new_t = &token_fmtp;
break;
case '#':
/* count this '#' only if there is no other one following. It might be part of a fmtp. */
for (p2 = pos + 1; p2 < str_end; p2++)
if (*p2 == '#')
break;
if (p2 < str_end && *p2 == '#')
break;
/* This is the last '#' in the string. Count it only when a digit follows. */
if (!isdigit(pos[1]))
break;
new_t = &token_payload_type;
break;
default:
break;
}
if (!new_t)
continue;
/* If we already have a token for a start character, don't start it again. These may be part of a fmtp
* string. */
if (new_t == t)
continue;
t->end = pos;
t = new_t;
t->start = pos + 1;
}
t->end = pos;
token_copy(dst, &dst->encoding_name, &token_encoding_name);
if (token_rate.start)
dst->rate = atoi(token_rate.start);
else
dst->rate = 8000;
token_copy(dst, &dst->fmtp, &token_fmtp);
if (token_payload_type.start)
dst->payload_type = atoi(token_payload_type.start);
return 0;
}
/* Compare both payload type number and fmtp string 1:1 */
const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_exact = {
.payload_type = true,
.encoding_name = true,
.rate = true,
.fmtp = OSMO_SDP_CMP_EXACT,
};
/* Ignore payload type number; compare fmtp string by meaning when possible, else 1:1 */
const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_equivalent = {
.payload_type = false,
.encoding_name = true,
.rate = true,
.fmtp = OSMO_SDP_CMP_EQUIVALENT,
};
/* Compare only encoding name */
const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_name = {
.payload_type = false,
.encoding_name = true,
.rate = false,
.fmtp = OSMO_SDP_CMP_IGNORE,
};
extern const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_equivalent;
static inline int strcmp_safe(const char *a, const char *b)
{
return strcmp(a ? : "", b ? : "");
}
/*! Compare encoding name, rate and fmtp, returning cmp result: -1 if a < b, 0 if a == b, 1 if a > b.
* Compare as defined in 'cmp':
* If cmpf->payload_type is false, ignore payload_type numbers.
* If cmpf->rate is false, ignore rate.
* If cmpf->fmtp is OSMO_SDP_CMP_IGNORE, ignore fmtp strings.
* If cmpf->fmtp is OSMO_SDP_CMP_EXACT, use strcmp() to match fmtp 1:1.
* If cmpf->fmtp is OSMO_SDP_CMP_EQUIVALENT, use specific fmtp knowledge to match equivalent entries;
* - AMR fmtp matching is done by osmo_sdp_fmtp_amr_match().
* - for all others, still compare fmtp 1:1.
*/
int osmo_sdp_codec_cmp(const struct osmo_sdp_codec *a, const struct osmo_sdp_codec *b,
const struct osmo_sdp_codec_cmp_flags *cmpf)
{
int cmp;
if (a == b)
return 0;
if (!a)
return -1;
if (!b)
return 1;
if (!cmpf)
cmpf = &osmo_sdp_codec_cmp_exact;
if (cmpf->encoding_name) {
cmp = strcmp_safe(a->encoding_name, b->encoding_name);
if (cmp)
return cmp;
}
if (cmpf->rate) {
cmp = OSMO_CMP(a->rate, b->rate);
if (cmp)
return cmp;
}
switch (cmpf->fmtp) {
default:
case OSMO_SDP_CMP_EXACT:
cmp = strcmp_safe(a->fmtp, b->fmtp);
break;
case OSMO_SDP_CMP_EQUIVALENT:
/* In case of AMR, allow logical matching; we only need to do that if the strings differ. */
cmp = strcmp_safe(a->fmtp, b->fmtp);
if (cmp
&& !strcmp_safe("AMR", a->encoding_name)
&& osmo_sdp_fmtp_amr_match(a->fmtp, b->fmtp))
cmp = 0;
break;
case OSMO_SDP_CMP_IGNORE:
cmp = 0;
break;
}
if (cmp)
return cmp;
if (cmpf->payload_type)
cmp = OSMO_CMP(a->payload_type, b->payload_type);
return cmp;
}

View File

@@ -0,0 +1,367 @@
/* Codec management in SDP messages. */
/*
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved.
*
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <string.h>
#include <osmocom/core/utils.h>
#include <osmocom/sdp/sdp_codec_list.h>
struct osmo_sdp_codec_list *osmo_sdp_codec_list_alloc(void *ctx)
{
struct osmo_sdp_codec_list *codec_list = talloc_zero(ctx, struct osmo_sdp_codec_list);
INIT_LLIST_HEAD(&codec_list->list);
return codec_list;
}
/*! Free all items contained in this list, do not free the list itself (leave an empty list). */
void osmo_sdp_codec_list_free_items(struct osmo_sdp_codec_list *codec_list)
{
struct osmo_sdp_codec *c;
while ((c = osmo_sdp_codec_list_first(codec_list))) {
osmo_sdp_codec_list_remove_entry(c);
talloc_free(c);
}
}
struct osmo_sdp_codec *osmo_sdp_codec_list_add_empty(struct osmo_sdp_codec_list *codec_list)
{
struct osmo_sdp_codec *c = osmo_sdp_codec_alloc(codec_list);
llist_add_tail(&c->entry, &codec_list->list);
return c;
}
int8_t osmo_sdp_codec_list_get_unused_dyn_pt_nr(const struct osmo_sdp_codec_list *codec_list, int8_t suggest_pt_nr)
{
bool present[127 - 96 + 1] = {};
const struct osmo_sdp_codec *c;
bool suggest_pt_nr_exists = false;
int i;
osmo_sdp_codec_list_foreach (c, codec_list) {
if (c->payload_type >= 96 && c->payload_type <= 127)
present[c->payload_type - 96] = true;
if (c->payload_type == suggest_pt_nr)
suggest_pt_nr_exists = true;
}
if (!suggest_pt_nr_exists)
return suggest_pt_nr;
/* The desired number is already taken, see which of the dynamic types is not taken yet */
for (i = 96; i <= 127; i++) {
/* For dynamic allocations, skip these predefined numbers, taken from enum mgcp_codecs:
* CODEC_GSMEFR_8000_1 = 110, 3GPP TS 48.103 table 5.4.2.2.1
* CODEC_GSMHR_8000_1 = 111, 3GPP TS 48.103 table 5.4.2.2.1
* CODEC_AMR_8000_1 = 112, 3GPP TS 48.103 table 5.4.2.2.1
* CODEC_AMRWB_16000_1 = 113, 3GPP TS 48.103 table 5.4.2.2.1
* CODEC_CLEARMODE = 120, 3GPP TS 48.103 table 5.4.2.2.1
*/
if (i >= 110 && i <= 113)
continue;
else if (i == 120)
continue;
if (!present[i - 96])
return i;
}
return -1;
}
/*! Allocate a new entry in codec_list and copy codec's values to it.
* If once is NULL, unconditionally add a new codec entry.
* If once is non-NULL, do not add a new entry when the list already contains a matching entry; for determining a match,
* use the once->flags. For example, if once = &osmo_sdp_codec_cmp_equivalent, look up if codec_list has a similar
* codec, and add the new entry only if it is not listed.
* See osmo_sdp_codec_cmp() and osmo_sdp_fmtp_amr_match() for details.
* Return the new entry, or the equivalent entry already present in the list.
*/
struct osmo_sdp_codec *osmo_sdp_codec_list_add(struct osmo_sdp_codec_list *codec_list,
const struct osmo_sdp_codec *codec,
const struct osmo_sdp_codec_cmp_flags *once, bool pick_unused_pt_nr)
{
struct osmo_sdp_codec *new_entry;
int8_t payload_type;
if (once) {
struct osmo_sdp_codec *c;
osmo_sdp_codec_list_foreach (c, codec_list)
if (!osmo_sdp_codec_cmp(codec, c, once))
return c;
}
/* Adjust payload_type number? */
payload_type = codec->payload_type;
if (pick_unused_pt_nr)
payload_type = osmo_sdp_codec_list_get_unused_dyn_pt_nr(codec_list, payload_type);
/* Take provided values, possibly modified payload_type */
new_entry = osmo_sdp_codec_list_add_empty(codec_list);
osmo_sdp_codec_set(new_entry, payload_type, codec->encoding_name, codec->rate, codec->fmtp);
return new_entry;
}
/*! Remove and free all entries from the codec_list that match the given codec according to osmo_sdp_codec_cmp(cmpf).
* Return the number of entries freed. */
int osmo_sdp_codec_list_remove(struct osmo_sdp_codec_list *codec_list, const struct osmo_sdp_codec *codec,
const struct osmo_sdp_codec_cmp_flags *cmpf)
{
struct osmo_sdp_codec *i, *j;
int count = 0;
osmo_sdp_codec_list_foreach_safe (i, j, codec_list) {
if (osmo_sdp_codec_cmp(i, codec, cmpf))
continue;
osmo_sdp_codec_list_remove_entry(i);
talloc_free(i);
count++;
}
return count;
}
/*! Unlink an osmo_sdp_codec from an osmo_sdp_codec_list, if the codec instance is part of a list. Do not free the
* struct osmo_sdp_codec.
*/
void osmo_sdp_codec_list_remove_entry(struct osmo_sdp_codec *codec)
{
/* The codec is not part of a list in these cases:
* After talloc_zero(), next == NULL.
* After llist_del(), next == LLIST_POISON1. */
if (codec->entry.next != NULL
&& codec->entry.next != (struct llist_head *)LLIST_POISON1)
llist_del(&codec->entry);
}
static inline int strcmp_safe(const char *a, const char *b)
{
return strcmp(a ? : "", b ? : "");
}
/*! Short single-line representation of a list of SDP audio codecs, convenient for logging.
* If summarize == true, collapse variants of the same encoding_name (in practice, don't show all of the various AMR
* fmtp permutations). If summarize == false, print each and every codec in full.
*/
int osmo_sdp_codec_list_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_codec_list *codec_list, bool summarize)
{
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
const struct osmo_sdp_codec *codec;
bool first;
if (llist_empty(&codec_list->list)) {
OSMO_STRBUF_PRINTF(sb, "(no-codecs)");
return sb.chars_needed;
}
if (!summarize) {
first = true;
osmo_sdp_codec_list_foreach (codec, codec_list) {
if (!first)
OSMO_STRBUF_PRINTF(sb, " ");
OSMO_STRBUF_APPEND(sb, osmo_sdp_codec_to_str_buf, codec);
first = false;
}
return sb.chars_needed;
}
/* summarize */
first = true;
osmo_sdp_codec_list_foreach (codec, codec_list) {
const struct osmo_sdp_codec *c2;
int count = 0;
bool various_pt = false;
/* When this encoding name has been handled before, skip it now. */
osmo_sdp_codec_list_foreach (c2, codec_list) {
if (c2 == codec)
break;
if (!strcmp_safe(codec->encoding_name, c2->encoding_name)) {
count = 1;
break;
}
}
if (count)
continue;
/* Not seen this encoding_name before, count total occurences */
count = 0;
osmo_sdp_codec_list_foreach (c2, codec_list) {
if (!strcmp_safe(codec->encoding_name, c2->encoding_name)) {
count++;
if (codec->payload_type != c2->payload_type)
various_pt = true;
}
}
if (!first)
OSMO_STRBUF_PRINTF(sb, " ");
if (count > 1)
OSMO_STRBUF_PRINTF(sb, "%d*", count);
OSMO_STRBUF_PRINTF(sb, "%s", codec->encoding_name);
if (!various_pt)
OSMO_STRBUF_PRINTF(sb, "#%d", codec->payload_type);
first = false;
}
return sb.chars_needed;
}
char *osmo_sdp_codec_list_to_str_c(void *ctx, const struct osmo_sdp_codec_list *codec_list, bool summarize)
{
OSMO_NAME_C_IMPL(ctx, 128, "osmo_sdp_codec_list_to_str_c-ERROR", osmo_sdp_codec_list_to_str_buf, codec_list, summarize)
}
/*! Return first entry, or NULL if the list is empty. */
struct osmo_sdp_codec *osmo_sdp_codec_list_first(const struct osmo_sdp_codec_list *list)
{
return llist_first_entry_or_null(&list->list, struct osmo_sdp_codec, entry);
}
/*! Move entries matching 'codec' to the front of the list. Matching is done via osmo_sdp_codec_cmp(cmpf).
* Return the number of matches that are now at the front of the list.
*/
int osmo_sdp_codec_list_move_to_first(struct osmo_sdp_codec_list *codec_list, const struct osmo_sdp_codec *codec,
const struct osmo_sdp_codec_cmp_flags *cmpf)
{
struct llist_head *head = &codec_list->list;
struct osmo_sdp_codec *i, *j;
int matches_found = 0;
osmo_sdp_codec_list_foreach_safe (i, j, codec_list) {
if (osmo_sdp_codec_cmp(codec, i, cmpf))
continue;
/* It's a match, move to the head */
osmo_sdp_codec_list_remove_entry(i);
llist_add(&i->entry, head);
matches_found++;
/* If more matches show up later, add them *after* the one just moved to the front. */
head = &i->entry;
}
return matches_found;
}
/*! Compare two lists of SDP codecs, returning cmp result: -1 if a < b, 0 if a == b, 1 if a > b.
* The two lists are compared in order, item by item, using osmo_sdp_codec_cmp(cmpf).
*/
int osmo_sdp_codec_list_cmp(const struct osmo_sdp_codec_list *a, const struct osmo_sdp_codec_list *b,
const struct osmo_sdp_codec_cmp_flags *cmpf)
{
const struct llist_head *a_start;
const struct llist_head *a_pos;
const struct llist_head *b_start;
const struct llist_head *b_pos;
int cmp;
/* NULL pointer == empty list */
if (a && llist_empty(&a->list))
a = NULL;
if (b && llist_empty(&a->list))
b = NULL;
/* are one or both empty? */
if (a == b)
return 0;
if (!a)
return -1;
if (!b)
return 1;
/* compare item by item */
a_start = &a->list;
a_pos = a_start->next;
b_start = &b->list;
b_pos = b_start->next;
for (; a_pos != a_start; a_pos = a_pos->next, b_pos = b_pos->next) {
const struct osmo_sdp_codec *codec_a;
const struct osmo_sdp_codec *codec_b;
if (b_pos == b_start) {
/* there is an entry in a, but b has already ended. mismatch. */
return 1;
}
codec_a = llist_entry(a_pos, struct osmo_sdp_codec, entry);
codec_b = llist_entry(b_pos, struct osmo_sdp_codec, entry);
cmp = osmo_sdp_codec_cmp(codec_a, codec_b, cmpf);
if (cmp)
return cmp;
}
if (b_pos != b_start) {
/* 'a' has ended, but 'b' has more items. mismatch. */
return -1;
}
/* full match. */
return 0;
}
/*! Leave only those codecs in 'dst' that are also present in 'other'.
* The matching is made by osmo_sdp_codec_cmp(cmpf).
* If translate_payload_type_numbers has an effect if 'dst' and 'other' have mismatching payload_type numbers for the
* same SDP codec descriptions. If translate_payload_type_numbers is true, take the payload_type numbers from 'other'.
* If false, keep payload_type numbers in 'dst' unchanged. */
void osmo_sdp_codec_list_intersection(struct osmo_sdp_codec_list *dst, const struct osmo_sdp_codec_list *other,
const struct osmo_sdp_codec_cmp_flags *cmpf,
bool translate_payload_type_numbers)
{
struct osmo_sdp_codec *i, *j;
osmo_sdp_codec_list_foreach_safe (i, j, dst) {
struct osmo_sdp_codec *o;
struct osmo_sdp_codec *match = NULL;
osmo_sdp_codec_list_foreach (o, other) {
if (osmo_sdp_codec_cmp(i, o, cmpf))
continue;
match = o;
break;
}
if (!match) {
osmo_sdp_codec_list_remove_entry(i);
talloc_free(i);
continue;
}
if (translate_payload_type_numbers)
i->payload_type = match->payload_type;
}
}
/* Find an entry for the given payload_type number in the given list of codecs. */
struct osmo_sdp_codec *osmo_sdp_codec_list_by_payload_type(struct osmo_sdp_codec_list *codec_list, int8_t payload_type)
{
struct osmo_sdp_codec *codec;
osmo_sdp_codec_list_foreach(codec, codec_list) {
if (codec->payload_type == payload_type)
return codec;
}
return NULL;
}
bool osmo_sdp_codec_list_is_empty(const struct osmo_sdp_codec_list *codec_list)
{
if (!codec_list)
return true;
return llist_empty(&codec_list->list);
}

View File

@@ -0,0 +1,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
View 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)
}

View File

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

View File

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

View File

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

View File

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

View File

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

@@ -0,0 +1,645 @@
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/sdp/sdp_codec_list.h>
#include <osmocom/sdp/fmtp.h>
void *test_ctx = NULL;
static void report_callback(const void *ptr, int depth, int max_depth, int is_ref, void *priv)
{
const char *name = talloc_get_name(ptr);
printf(" |%*s%3zu %s\n", depth, "", talloc_total_blocks(ptr), name);
}
/* Print a talloc report that is reproducible for test output verification. It contains no pointer addresses. */
#define report(CTX) _report(CTX, #CTX)
static void _report(void *ctx, const char *label)
{
fflush(stdout);
fflush(stderr);
printf("%s\n", label);
talloc_report_depth_cb(ctx, 0, 100, report_callback, NULL);
fflush(stdout);
}
struct codec_test {
struct osmo_sdp_codec set;
int expect_rc;
const char *expect_str;
bool expect_is_set;
};
struct codec_test codec_tests[] = {
{
.set = { 23, "encoding-name", 8000, NULL },
.expect_str = "encoding-name#23",
.expect_is_set = true,
},
{
.set = { 112, "AMR", 8000, "octet-align=1;mode-set=0,2,4" },
.expect_str = "AMR:octet-align=1;mode-set=0,2,4#112",
.expect_is_set = true,
},
{
.set = { 96, "AMR", 8000, "mode-set=0,2,4;octet-align=1" },
.expect_str = "AMR:mode-set=0,2,4;octet-align=1#96",
.expect_is_set = true,
},
{
.set = { 114, "AMR", 8000, "mode-set=0,2,4" },
.expect_str = "AMR:mode-set=0,2,4#114",
.expect_is_set = true,
},
{
.set = { 97, "AMR", 8000, "mode-set=0,2,4;octet-align=0" },
.expect_str = "AMR:mode-set=0,2,4;octet-align=0#97",
.expect_is_set = true,
},
{
.set = { 98, "AMR", 8000, "octet-align=1" },
.expect_str = "AMR:octet-align=1#98",
.expect_is_set = true,
},
{
.set = { 96, "AMR-WB", 16000 },
.expect_str = "AMR-WB/16000#96",
.expect_is_set = true,
},
{
.set = { 3, "GSM", 8000 },
.expect_str = "GSM#3",
.expect_is_set = true,
},
{
.set = { },
.expect_str = "/0#0",
.expect_is_set = false,
},
{
.set = { 112, NULL, 8000, "octet-align=1" },
.expect_str = ":octet-align=1#112",
.expect_is_set = false,
},
{
.set = { 112, "", 8000, "octet-align=1" },
.expect_str = ":octet-align=1#112",
.expect_is_set = false,
},
};
void test_codec(void)
{
void *ctx = talloc_named_const(test_ctx, 0, __func__);
struct codec_test *t;
struct codec_test *t2;
printf("\n\n--- %s()\n", __func__);
printf("- osmo_sdp_codec_set():\n");
for (t = codec_tests; (t - codec_tests) < ARRAY_SIZE(codec_tests); t++) {
struct osmo_sdp_codec *codec = osmo_sdp_codec_alloc(ctx);
char *str;
bool is_set;
osmo_sdp_codec_set(codec, t->set.payload_type, t->set.encoding_name, t->set.rate, t->set.fmtp);
str = osmo_sdp_codec_to_str_c(ctx, codec);
printf("osmo_sdp_codec_set [%d] '%s'\n", (int)(t - codec_tests), str);
if (strcmp(str, t->expect_str))
printf(" *** ERROR: expected '%s'\n", t->expect_str);
if (!osmo_sdp_codec_cmp(codec, &t->set, &osmo_sdp_codec_cmp_exact))
printf(" osmo_sdp_codec_cmp() ok\n");
else
printf(" osmo_sdp_codec_cmp() *** ERROR: mismatches original values\n");
is_set = osmo_sdp_codec_is_set(codec);
printf(" osmo_sdp_codec_is_set() = %s\n", is_set ? "true" : "false");
if (is_set != t->expect_is_set)
printf(" *** ERROR: expected is_set = %s\n", t->expect_is_set ? "true" : "false");
if (is_set != osmo_sdp_codec_is_set(&t->set))
printf(" *** ERROR: is_set(copy) != is_set(orig)\n");
talloc_free(str);
talloc_free(codec);
if (talloc_total_blocks(ctx) != 1)
printf(" *** ERROR: ctx has %zu items, should be 1\n", talloc_total_blocks(ctx));
}
printf("\n- osmo_sdp_codec_cmp(equivalent):\n");
for (t = codec_tests; (t - codec_tests) < ARRAY_SIZE(codec_tests); t++) {
for (t2 = codec_tests; (t2 - codec_tests) < ARRAY_SIZE(codec_tests); t2++) {
int cmp = osmo_sdp_codec_cmp(&t->set, &t2->set, &osmo_sdp_codec_cmp_equivalent);
int reverse_cmp = osmo_sdp_codec_cmp(&t2->set, &t->set, &osmo_sdp_codec_cmp_equivalent);
printf(" %s %s %s %s %s\n",
osmo_sdp_codec_to_str_c(ctx, &t->set),
(cmp == 0) ? "=="
: ((cmp < 0) ? "<" : ">"),
osmo_sdp_codec_to_str_c(ctx, &t2->set),
(reverse_cmp == 0) ? "=="
: ((reverse_cmp < 0) ? "<" : ">"),
osmo_sdp_codec_to_str_c(ctx, &t->set));
if (reverse_cmp != -cmp)
printf(" *** ERROR: osmo_sdp_codec_cmp(reverse args) == %d, expected %d\n",
reverse_cmp, -cmp);
talloc_free_children(ctx);
}
}
printf("\n- osmo_sdp_codec_from_str():\n");
for (t = codec_tests; (t - codec_tests) < ARRAY_SIZE(codec_tests); t++) {
struct osmo_sdp_codec *codec = osmo_sdp_codec_alloc(ctx);
int rc = osmo_sdp_codec_from_str(codec, t->expect_str, -1);
printf(" osmo_sdp_codec_from_str('%s') rc=%d",
t->expect_str, rc);
if (!rc) {
printf(" res=%s", osmo_sdp_codec_to_str_c(ctx, codec));
rc = osmo_sdp_codec_cmp(codec, &t->set, &osmo_sdp_codec_cmp_exact);
if (rc)
printf(" *** ERROR: osmo_sdp_codec_cmp(res,orig) = %d", rc);
}
printf("\n");
talloc_free_children(ctx);
}
talloc_free(ctx);
}
void test_codec_list(void)
{
void *list_ctx = talloc_named_const(test_ctx, 0, __func__);
void *print_ctx = talloc_named_const(test_ctx, 0, "print");
int i;
int rc;
struct osmo_sdp_codec *codec;
const struct osmo_sdp_codec all_codecs[] = {
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
};
struct osmo_sdp_codec_list *codec_list;
printf("\n\n--- %s()\n", __func__);
codec_list = osmo_sdp_codec_list_alloc(list_ctx);
printf("osmo_sdp_codec_list_first() = %s\n",
osmo_sdp_codec_to_str_c(print_ctx, osmo_sdp_codec_list_first(codec_list)));
report(list_ctx);
for (i = 0; i < ARRAY_SIZE(all_codecs); i++) {
struct osmo_sdp_codec *added = osmo_sdp_codec_list_add(codec_list, &all_codecs[i], NULL, false);
printf("[%d] osmo_sdp_codec_list_add(%s)\n", i, osmo_sdp_codec_to_str_c(print_ctx, added));
}
i = 0;
osmo_sdp_codec_list_foreach(codec, codec_list) {
printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
}
printf("osmo_sdp_codec_list_first() = %s\n",
osmo_sdp_codec_to_str_c(print_ctx, osmo_sdp_codec_list_first(codec_list)));
report(list_ctx);
printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
printf("\n");
printf("- add same entries again with once=exact, nothing should change\n");
for (i = 0; i < ARRAY_SIZE(all_codecs); i++) {
struct osmo_sdp_codec *added = osmo_sdp_codec_list_add(codec_list, &all_codecs[i],
&osmo_sdp_codec_cmp_exact, false);
printf("[] osmo_sdp_codec_list_add(%s)\n", osmo_sdp_codec_to_str_c(print_ctx, added));
}
i = 0;
osmo_sdp_codec_list_foreach(codec, codec_list) {
printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
}
report(list_ctx);
printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
printf("\n");
printf("- add same entries again with once=NULL, duplicates are added\n");
for (i = 0; i < ARRAY_SIZE(all_codecs); i++) {
struct osmo_sdp_codec *added = osmo_sdp_codec_list_add(codec_list, &all_codecs[i], NULL, false);
printf("[] osmo_sdp_codec_list_add(%s)\n", osmo_sdp_codec_to_str_c(print_ctx, added));
}
i = 0;
osmo_sdp_codec_list_foreach(codec, codec_list) {
printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
}
report(list_ctx);
printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
printf("\n");
printf("- add same entries again with once=NULL,pick_unused_pt_nr=true, duplicates are added with new #nr\n");
for (i = 0; i < ARRAY_SIZE(all_codecs); i++) {
struct osmo_sdp_codec *added = osmo_sdp_codec_list_add(codec_list, &all_codecs[i], NULL, true);
printf("[] osmo_sdp_codec_list_add(%s)\n", osmo_sdp_codec_to_str_c(print_ctx, added));
}
i = 0;
osmo_sdp_codec_list_foreach(codec, codec_list) {
printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
}
report(list_ctx);
printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
printf("\n");
printf("- remove all 'GSM#3' entries, with osmo_sdp_codec_cmp_exact\n");
rc = osmo_sdp_codec_list_remove(codec_list, &all_codecs[1], &osmo_sdp_codec_cmp_exact);
printf(" osmo_sdp_codec_list_remove() = %d\n", rc);
i = 0;
osmo_sdp_codec_list_foreach(codec, codec_list) {
printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
}
report(list_ctx);
printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
printf("- remove all 'GSM' entries, with osmo_sdp_codec_cmp_equivalent\n");
rc = osmo_sdp_codec_list_remove(codec_list, &all_codecs[1], &osmo_sdp_codec_cmp_equivalent);
printf(" osmo_sdp_codec_list_remove() = %d\n", rc);
i = 0;
osmo_sdp_codec_list_foreach(codec, codec_list) {
printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
}
report(list_ctx);
printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
rc = osmo_sdp_codec_list_move_to_first(codec_list, &all_codecs[0], &osmo_sdp_codec_cmp_equivalent);
printf("- osmo_sdp_codec_list_move_to_first('%s', equivalent) = %d\n",
osmo_sdp_codec_to_str_c(print_ctx, &all_codecs[0]), rc);
i = 0;
osmo_sdp_codec_list_foreach(codec, codec_list) {
printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
}
report(list_ctx);
printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
printf("- osmo_sdp_codec_list_free_items()\n");
osmo_sdp_codec_list_free_items(codec_list);
i = 0;
osmo_sdp_codec_list_foreach(codec, codec_list) {
printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
}
printf(" %d entries\n", i);
report(list_ctx);
printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
talloc_free(print_ctx);
talloc_free(list_ctx);
}
static struct osmo_sdp_codec_list *init_codec_list(void *ctx, const struct osmo_sdp_codec *init_array)
{
struct osmo_sdp_codec_list *dst = osmo_sdp_codec_list_alloc(ctx);
for (; osmo_sdp_codec_is_set(init_array); init_array++)
osmo_sdp_codec_list_add(dst, init_array, NULL, false);
return dst;
}
void test_codec_list_cmp(void)
{
void *ctx = talloc_named_const(test_ctx, 0, __func__);
void *print_ctx = talloc_named_const(test_ctx, 0, "print");
int i;
const struct osmo_sdp_codec codec_a[] = {
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
{}
};
const struct osmo_sdp_codec codec_b[][5] = {
/* same */
{
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
},
/* different payload_type */
{
{ .payload_type = 96, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
},
/* AMR fmtp in different order */
{
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "mode-set=0,2,4;octet-align=1" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
},
/* different AMR mode-set */
{
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=7" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
},
/* empty AMR mode-set */
{
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
},
/* different AMR octet-align */
{
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=0;mode-set=0,2,4" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
},
/* omitted AMR octet-align is identical to octet-align=0 */
{
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "mode-set=0,2,4" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
},
/* different order */
{
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
},
/* one less item */
{
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
},
/* one more item */
{
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
{ .payload_type = 110, .encoding_name = "GSM-EFR", .rate = 8000 },
},
};
const struct osmo_sdp_codec_cmp_flags *test_cmpf[] = {
&osmo_sdp_codec_cmp_name,
&osmo_sdp_codec_cmp_equivalent,
&osmo_sdp_codec_cmp_exact,
};
printf("\n\n--- %s()\n", __func__);
for (i = 0; i < ARRAY_SIZE(codec_b); i++) {
struct osmo_sdp_codec_list *list_a = init_codec_list(ctx, codec_a);
struct osmo_sdp_codec_list *list_b = init_codec_list(ctx, codec_b[i]);
int j;
printf("A = %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, list_a, false));
printf("B = %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, list_b, false));
for (j = 0; j < ARRAY_SIZE(test_cmpf); j++) {
const struct osmo_sdp_codec_cmp_flags *cmpf = test_cmpf[j];
int cmp = osmo_sdp_codec_list_cmp(list_a, list_b, cmpf);
int reverse_cmp = osmo_sdp_codec_list_cmp(list_b, list_a, cmpf);
printf(" cmpf[%d]: payload_type=%s rate=%s fmtp=%d: A %s B %s A\n",
j,
cmpf->payload_type ? "true" : "false",
cmpf->rate ? "true" : "false",
cmpf->fmtp,
(cmp == 0) ? "=="
: ((cmp < 0) ? "<" : ">"),
(reverse_cmp == 0) ? "=="
: ((reverse_cmp < 0) ? "<" : ">"));
if (reverse_cmp != -cmp)
printf(" *** ERROR: osmo_sdp_codec_list_cmp(reverse args) == %d, expected %d\n",
reverse_cmp, -cmp);
}
talloc_free(list_a);
talloc_free(list_b);
if (talloc_total_blocks(ctx) != 1) {
printf("ERROR: memleak:\n");
report(ctx);
}
printf("\n");
talloc_free_children(print_ctx);
}
talloc_free(print_ctx);
talloc_free(ctx);
}
void test_codec_list_intersection(void)
{
void *ctx = talloc_named_const(test_ctx, 0, __func__);
void *print_ctx = talloc_named_const(test_ctx, 0, "print");
int i;
const struct osmo_sdp_codec codec_a[] = {
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
{}
};
const struct osmo_sdp_codec codec_b[][5] = {
/* same */
{
{ .payload_type = 96, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
{ .payload_type = 97, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 98, .encoding_name = "GSM-HR-08", .rate = 8000 },
},
/* same in different order */
{
{ .payload_type = 98, .encoding_name = "GSM-HR-08", .rate = 8000 },
{ .payload_type = 96, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
{ .payload_type = 97, .encoding_name = "GSM", .rate = 8000 },
},
/* two matches */
{
{ .payload_type = 98, .encoding_name = "GSM-HR-08", .rate = 8000 },
{ .payload_type = 110, .encoding_name = "GSM-EFR", .rate = 8000 },
{ .payload_type = 97, .encoding_name = "GSM", .rate = 8000 },
},
/* no match */
{
{ .payload_type = 97, .encoding_name = "AMR-WB", .rate = 16000 },
{ .payload_type = 98, .encoding_name = "FOO", .rate = 8000 },
{ .payload_type = 110, .encoding_name = "GSM-EFR", .rate = 8000 },
},
};
printf("\n\n--- %s()\n", __func__);
for (i = 0; i < ARRAY_SIZE(codec_b); i++) {
struct osmo_sdp_codec_list *list_a = init_codec_list(ctx, codec_a);
struct osmo_sdp_codec_list *list_b = init_codec_list(ctx, codec_b[i]);
printf("A = %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, list_a, false));
printf("B = %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, list_b, false));
osmo_sdp_codec_list_intersection(list_a, list_b, &osmo_sdp_codec_cmp_equivalent, false);
printf("osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=false)\n = %s\n",
osmo_sdp_codec_list_to_str_c(print_ctx, list_a, false));
talloc_free(list_a);
list_a = init_codec_list(ctx, codec_a);
osmo_sdp_codec_list_intersection(list_a, list_b, &osmo_sdp_codec_cmp_equivalent, true);
printf("osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=true)\n = %s\n",
osmo_sdp_codec_list_to_str_c(print_ctx, list_a, false));
talloc_free(list_a);
list_a = init_codec_list(ctx, codec_a);
osmo_sdp_codec_list_intersection(list_b, list_a, &osmo_sdp_codec_cmp_equivalent, false);
printf("osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=false)\n = %s\n",
osmo_sdp_codec_list_to_str_c(print_ctx, list_b, false));
talloc_free(list_a);
list_a = init_codec_list(ctx, codec_a);
osmo_sdp_codec_list_intersection(list_b, list_a, &osmo_sdp_codec_cmp_equivalent, true);
printf("osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=true)\n = %s\n",
osmo_sdp_codec_list_to_str_c(print_ctx, list_b, false));
talloc_free(list_a);
talloc_free(list_b);
if (talloc_total_blocks(ctx) != 1) {
printf("ERROR: memleak:\n");
report(ctx);
}
printf("\n");
talloc_free_children(print_ctx);
}
talloc_free(print_ctx);
talloc_free(ctx);
}
struct my_obj {
struct osmo_sdp_codec *codec;
struct osmo_sdp_codec_list *codec_list;
};
struct my_obj *my_obj_alloc(void *ctx)
{
struct my_obj *o = talloc_zero(ctx, struct my_obj);
o->codec_list = osmo_sdp_codec_list_alloc(o);
return o;
}
void test_obj_members(void)
{
void *ctx = talloc_named_const(test_ctx, 0, __func__);
void *print_ctx = talloc_named_const(test_ctx, 0, "print");
int i;
struct osmo_sdp_codec *codec;
struct my_obj *o;
printf("\n\n--- %s()\n", __func__);
o = my_obj_alloc(ctx);
o->codec = osmo_sdp_codec_alloc(o);
osmo_sdp_codec_set(o->codec, 96, "AMR", 8000, "octet-align=1");
printf("o->codec = %s\n", osmo_sdp_codec_to_str_c(print_ctx, o->codec));
report(ctx);
osmo_sdp_codec_list_add(o->codec_list, o->codec, false, false);
osmo_sdp_codec_list_add(o->codec_list, o->codec, false, true);
i = 0;
osmo_sdp_codec_list_foreach(codec, o->codec_list) {
printf("o->codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
}
report(ctx);
printf("talloc_free(o)\n");
talloc_free(o);
report(ctx);
talloc_free(ctx);
talloc_free(print_ctx);
}
typedef void (*test_func_t)(void);
test_func_t test_func[] = {
test_codec,
test_codec_list,
test_codec_list_cmp,
test_codec_list_intersection,
test_obj_members,
};
int main(void)
{
int i;
test_ctx = talloc_named_const(NULL, 0, "sdp_codec_test");
for (i = 0; i < ARRAY_SIZE(test_func); i++) {
test_func[i]();
if (talloc_total_blocks(test_ctx) != 1) {
talloc_report_full(test_ctx, stderr);
printf("ERROR after test %d: memory leak\n", i);
return -1;
}
}
talloc_free(test_ctx);
return 0;
}

View File

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

@@ -0,0 +1,538 @@
--- test_codec()
- osmo_sdp_codec_set():
osmo_sdp_codec_set [0] 'encoding-name#23'
osmo_sdp_codec_cmp() ok
osmo_sdp_codec_is_set() = true
osmo_sdp_codec_set [1] 'AMR:octet-align=1;mode-set=0,2,4#112'
osmo_sdp_codec_cmp() ok
osmo_sdp_codec_is_set() = true
osmo_sdp_codec_set [2] 'AMR:mode-set=0,2,4;octet-align=1#96'
osmo_sdp_codec_cmp() ok
osmo_sdp_codec_is_set() = true
osmo_sdp_codec_set [3] 'AMR:mode-set=0,2,4#114'
osmo_sdp_codec_cmp() ok
osmo_sdp_codec_is_set() = true
osmo_sdp_codec_set [4] 'AMR:mode-set=0,2,4;octet-align=0#97'
osmo_sdp_codec_cmp() ok
osmo_sdp_codec_is_set() = true
osmo_sdp_codec_set [5] 'AMR:octet-align=1#98'
osmo_sdp_codec_cmp() ok
osmo_sdp_codec_is_set() = true
osmo_sdp_codec_set [6] 'AMR-WB/16000#96'
osmo_sdp_codec_cmp() ok
osmo_sdp_codec_is_set() = true
osmo_sdp_codec_set [7] 'GSM#3'
osmo_sdp_codec_cmp() ok
osmo_sdp_codec_is_set() = true
osmo_sdp_codec_set [8] '/0#0'
osmo_sdp_codec_cmp() ok
osmo_sdp_codec_is_set() = false
osmo_sdp_codec_set [9] ':octet-align=1#112'
osmo_sdp_codec_cmp() ok
osmo_sdp_codec_is_set() = false
osmo_sdp_codec_set [10] ':octet-align=1#112'
osmo_sdp_codec_cmp() ok
osmo_sdp_codec_is_set() = false
- osmo_sdp_codec_cmp(equivalent):
encoding-name#23 == encoding-name#23 == encoding-name#23
encoding-name#23 > AMR:octet-align=1;mode-set=0,2,4#112 < encoding-name#23
encoding-name#23 > AMR:mode-set=0,2,4;octet-align=1#96 < encoding-name#23
encoding-name#23 > AMR:mode-set=0,2,4#114 < encoding-name#23
encoding-name#23 > AMR:mode-set=0,2,4;octet-align=0#97 < encoding-name#23
encoding-name#23 > AMR:octet-align=1#98 < encoding-name#23
encoding-name#23 > AMR-WB/16000#96 < encoding-name#23
encoding-name#23 > GSM#3 < encoding-name#23
encoding-name#23 > /0#0 < encoding-name#23
encoding-name#23 > :octet-align=1#112 < encoding-name#23
encoding-name#23 > :octet-align=1#112 < encoding-name#23
AMR:octet-align=1;mode-set=0,2,4#112 < encoding-name#23 > AMR:octet-align=1;mode-set=0,2,4#112
AMR:octet-align=1;mode-set=0,2,4#112 == AMR:octet-align=1;mode-set=0,2,4#112 == AMR:octet-align=1;mode-set=0,2,4#112
AMR:octet-align=1;mode-set=0,2,4#112 == AMR:mode-set=0,2,4;octet-align=1#96 == AMR:octet-align=1;mode-set=0,2,4#112
AMR:octet-align=1;mode-set=0,2,4#112 > AMR:mode-set=0,2,4#114 < AMR:octet-align=1;mode-set=0,2,4#112
AMR:octet-align=1;mode-set=0,2,4#112 > AMR:mode-set=0,2,4;octet-align=0#97 < AMR:octet-align=1;mode-set=0,2,4#112
AMR:octet-align=1;mode-set=0,2,4#112 == AMR:octet-align=1#98 == AMR:octet-align=1;mode-set=0,2,4#112
AMR:octet-align=1;mode-set=0,2,4#112 < AMR-WB/16000#96 > AMR:octet-align=1;mode-set=0,2,4#112
AMR:octet-align=1;mode-set=0,2,4#112 < GSM#3 > AMR:octet-align=1;mode-set=0,2,4#112
AMR:octet-align=1;mode-set=0,2,4#112 > /0#0 < AMR:octet-align=1;mode-set=0,2,4#112
AMR:octet-align=1;mode-set=0,2,4#112 > :octet-align=1#112 < AMR:octet-align=1;mode-set=0,2,4#112
AMR:octet-align=1;mode-set=0,2,4#112 > :octet-align=1#112 < AMR:octet-align=1;mode-set=0,2,4#112
AMR:mode-set=0,2,4;octet-align=1#96 < encoding-name#23 > AMR:mode-set=0,2,4;octet-align=1#96
AMR:mode-set=0,2,4;octet-align=1#96 == AMR:octet-align=1;mode-set=0,2,4#112 == AMR:mode-set=0,2,4;octet-align=1#96
AMR:mode-set=0,2,4;octet-align=1#96 == AMR:mode-set=0,2,4;octet-align=1#96 == AMR:mode-set=0,2,4;octet-align=1#96
AMR:mode-set=0,2,4;octet-align=1#96 > AMR:mode-set=0,2,4#114 < AMR:mode-set=0,2,4;octet-align=1#96
AMR:mode-set=0,2,4;octet-align=1#96 > AMR:mode-set=0,2,4;octet-align=0#97 < AMR:mode-set=0,2,4;octet-align=1#96
AMR:mode-set=0,2,4;octet-align=1#96 == AMR:octet-align=1#98 == AMR:mode-set=0,2,4;octet-align=1#96
AMR:mode-set=0,2,4;octet-align=1#96 < AMR-WB/16000#96 > AMR:mode-set=0,2,4;octet-align=1#96
AMR:mode-set=0,2,4;octet-align=1#96 < GSM#3 > AMR:mode-set=0,2,4;octet-align=1#96
AMR:mode-set=0,2,4;octet-align=1#96 > /0#0 < AMR:mode-set=0,2,4;octet-align=1#96
AMR:mode-set=0,2,4;octet-align=1#96 > :octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=1#96
AMR:mode-set=0,2,4;octet-align=1#96 > :octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=1#96
AMR:mode-set=0,2,4#114 < encoding-name#23 > AMR:mode-set=0,2,4#114
AMR:mode-set=0,2,4#114 < AMR:octet-align=1;mode-set=0,2,4#112 > AMR:mode-set=0,2,4#114
AMR:mode-set=0,2,4#114 < AMR:mode-set=0,2,4;octet-align=1#96 > AMR:mode-set=0,2,4#114
AMR:mode-set=0,2,4#114 == AMR:mode-set=0,2,4#114 == AMR:mode-set=0,2,4#114
AMR:mode-set=0,2,4#114 == AMR:mode-set=0,2,4;octet-align=0#97 == AMR:mode-set=0,2,4#114
AMR:mode-set=0,2,4#114 < AMR:octet-align=1#98 > AMR:mode-set=0,2,4#114
AMR:mode-set=0,2,4#114 < AMR-WB/16000#96 > AMR:mode-set=0,2,4#114
AMR:mode-set=0,2,4#114 < GSM#3 > AMR:mode-set=0,2,4#114
AMR:mode-set=0,2,4#114 > /0#0 < AMR:mode-set=0,2,4#114
AMR:mode-set=0,2,4#114 > :octet-align=1#112 < AMR:mode-set=0,2,4#114
AMR:mode-set=0,2,4#114 > :octet-align=1#112 < AMR:mode-set=0,2,4#114
AMR:mode-set=0,2,4;octet-align=0#97 < encoding-name#23 > AMR:mode-set=0,2,4;octet-align=0#97
AMR:mode-set=0,2,4;octet-align=0#97 < AMR:octet-align=1;mode-set=0,2,4#112 > AMR:mode-set=0,2,4;octet-align=0#97
AMR:mode-set=0,2,4;octet-align=0#97 < AMR:mode-set=0,2,4;octet-align=1#96 > AMR:mode-set=0,2,4;octet-align=0#97
AMR:mode-set=0,2,4;octet-align=0#97 == AMR:mode-set=0,2,4#114 == AMR:mode-set=0,2,4;octet-align=0#97
AMR:mode-set=0,2,4;octet-align=0#97 == AMR:mode-set=0,2,4;octet-align=0#97 == AMR:mode-set=0,2,4;octet-align=0#97
AMR:mode-set=0,2,4;octet-align=0#97 < AMR:octet-align=1#98 > AMR:mode-set=0,2,4;octet-align=0#97
AMR:mode-set=0,2,4;octet-align=0#97 < AMR-WB/16000#96 > AMR:mode-set=0,2,4;octet-align=0#97
AMR:mode-set=0,2,4;octet-align=0#97 < GSM#3 > AMR:mode-set=0,2,4;octet-align=0#97
AMR:mode-set=0,2,4;octet-align=0#97 > /0#0 < AMR:mode-set=0,2,4;octet-align=0#97
AMR:mode-set=0,2,4;octet-align=0#97 > :octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=0#97
AMR:mode-set=0,2,4;octet-align=0#97 > :octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=0#97
AMR:octet-align=1#98 < encoding-name#23 > AMR:octet-align=1#98
AMR:octet-align=1#98 == AMR:octet-align=1;mode-set=0,2,4#112 == AMR:octet-align=1#98
AMR:octet-align=1#98 == AMR:mode-set=0,2,4;octet-align=1#96 == AMR:octet-align=1#98
AMR:octet-align=1#98 > AMR:mode-set=0,2,4#114 < AMR:octet-align=1#98
AMR:octet-align=1#98 > AMR:mode-set=0,2,4;octet-align=0#97 < AMR:octet-align=1#98
AMR:octet-align=1#98 == AMR:octet-align=1#98 == AMR:octet-align=1#98
AMR:octet-align=1#98 < AMR-WB/16000#96 > AMR:octet-align=1#98
AMR:octet-align=1#98 < GSM#3 > AMR:octet-align=1#98
AMR:octet-align=1#98 > /0#0 < AMR:octet-align=1#98
AMR:octet-align=1#98 > :octet-align=1#112 < AMR:octet-align=1#98
AMR:octet-align=1#98 > :octet-align=1#112 < AMR:octet-align=1#98
AMR-WB/16000#96 < encoding-name#23 > AMR-WB/16000#96
AMR-WB/16000#96 > AMR:octet-align=1;mode-set=0,2,4#112 < AMR-WB/16000#96
AMR-WB/16000#96 > AMR:mode-set=0,2,4;octet-align=1#96 < AMR-WB/16000#96
AMR-WB/16000#96 > AMR:mode-set=0,2,4#114 < AMR-WB/16000#96
AMR-WB/16000#96 > AMR:mode-set=0,2,4;octet-align=0#97 < AMR-WB/16000#96
AMR-WB/16000#96 > AMR:octet-align=1#98 < AMR-WB/16000#96
AMR-WB/16000#96 == AMR-WB/16000#96 == AMR-WB/16000#96
AMR-WB/16000#96 < GSM#3 > AMR-WB/16000#96
AMR-WB/16000#96 > /0#0 < AMR-WB/16000#96
AMR-WB/16000#96 > :octet-align=1#112 < AMR-WB/16000#96
AMR-WB/16000#96 > :octet-align=1#112 < AMR-WB/16000#96
GSM#3 < encoding-name#23 > GSM#3
GSM#3 > AMR:octet-align=1;mode-set=0,2,4#112 < GSM#3
GSM#3 > AMR:mode-set=0,2,4;octet-align=1#96 < GSM#3
GSM#3 > AMR:mode-set=0,2,4#114 < GSM#3
GSM#3 > AMR:mode-set=0,2,4;octet-align=0#97 < GSM#3
GSM#3 > AMR:octet-align=1#98 < GSM#3
GSM#3 > AMR-WB/16000#96 < GSM#3
GSM#3 == GSM#3 == GSM#3
GSM#3 > /0#0 < GSM#3
GSM#3 > :octet-align=1#112 < GSM#3
GSM#3 > :octet-align=1#112 < GSM#3
/0#0 < encoding-name#23 > /0#0
/0#0 < AMR:octet-align=1;mode-set=0,2,4#112 > /0#0
/0#0 < AMR:mode-set=0,2,4;octet-align=1#96 > /0#0
/0#0 < AMR:mode-set=0,2,4#114 > /0#0
/0#0 < AMR:mode-set=0,2,4;octet-align=0#97 > /0#0
/0#0 < AMR:octet-align=1#98 > /0#0
/0#0 < AMR-WB/16000#96 > /0#0
/0#0 < GSM#3 > /0#0
/0#0 == /0#0 == /0#0
/0#0 < :octet-align=1#112 > /0#0
/0#0 < :octet-align=1#112 > /0#0
:octet-align=1#112 < encoding-name#23 > :octet-align=1#112
:octet-align=1#112 < AMR:octet-align=1;mode-set=0,2,4#112 > :octet-align=1#112
:octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=1#96 > :octet-align=1#112
:octet-align=1#112 < AMR:mode-set=0,2,4#114 > :octet-align=1#112
:octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=0#97 > :octet-align=1#112
:octet-align=1#112 < AMR:octet-align=1#98 > :octet-align=1#112
:octet-align=1#112 < AMR-WB/16000#96 > :octet-align=1#112
:octet-align=1#112 < GSM#3 > :octet-align=1#112
:octet-align=1#112 > /0#0 < :octet-align=1#112
:octet-align=1#112 == :octet-align=1#112 == :octet-align=1#112
:octet-align=1#112 == :octet-align=1#112 == :octet-align=1#112
:octet-align=1#112 < encoding-name#23 > :octet-align=1#112
:octet-align=1#112 < AMR:octet-align=1;mode-set=0,2,4#112 > :octet-align=1#112
:octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=1#96 > :octet-align=1#112
:octet-align=1#112 < AMR:mode-set=0,2,4#114 > :octet-align=1#112
:octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=0#97 > :octet-align=1#112
:octet-align=1#112 < AMR:octet-align=1#98 > :octet-align=1#112
:octet-align=1#112 < AMR-WB/16000#96 > :octet-align=1#112
:octet-align=1#112 < GSM#3 > :octet-align=1#112
:octet-align=1#112 > /0#0 < :octet-align=1#112
:octet-align=1#112 == :octet-align=1#112 == :octet-align=1#112
:octet-align=1#112 == :octet-align=1#112 == :octet-align=1#112
- osmo_sdp_codec_from_str():
osmo_sdp_codec_from_str('encoding-name#23') rc=0 res=encoding-name#23
osmo_sdp_codec_from_str('AMR:octet-align=1;mode-set=0,2,4#112') rc=0 res=AMR:octet-align=1;mode-set=0,2,4#112
osmo_sdp_codec_from_str('AMR:mode-set=0,2,4;octet-align=1#96') rc=0 res=AMR:mode-set=0,2,4;octet-align=1#96
osmo_sdp_codec_from_str('AMR:mode-set=0,2,4#114') rc=0 res=AMR:mode-set=0,2,4#114
osmo_sdp_codec_from_str('AMR:mode-set=0,2,4;octet-align=0#97') rc=0 res=AMR:mode-set=0,2,4;octet-align=0#97
osmo_sdp_codec_from_str('AMR:octet-align=1#98') rc=0 res=AMR:octet-align=1#98
osmo_sdp_codec_from_str('AMR-WB/16000#96') rc=0 res=AMR-WB/16000#96
osmo_sdp_codec_from_str('GSM#3') rc=0 res=GSM#3
osmo_sdp_codec_from_str('/0#0') rc=0 res=/0#0
osmo_sdp_codec_from_str(':octet-align=1#112') rc=0 res=:octet-align=1#112
osmo_sdp_codec_from_str(':octet-align=1#112') rc=0 res=:octet-align=1#112
--- test_codec_list()
osmo_sdp_codec_list_first() = NULL
list_ctx
| 2 test_codec_list
| 1 struct osmo_sdp_codec_list
[0] osmo_sdp_codec_list_add(AMR:octet-align=1;mode-set=0,2,4#112)
[1] osmo_sdp_codec_list_add(GSM#3)
[2] osmo_sdp_codec_list_add(GSM-HR-08#111)
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[1] = GSM#3
codec_list[2] = GSM-HR-08#111
osmo_sdp_codec_list_first() = AMR:octet-align=1;mode-set=0,2,4#112
list_ctx
| 9 test_codec_list
| 8 struct osmo_sdp_codec_list
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 2 struct osmo_sdp_codec
| 1 GSM
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
osmo_sdp_codec_list_to_str_c(summarize=true):
'AMR#112 GSM#3 GSM-HR-08#111'
osmo_sdp_codec_list_to_str_c(summarize=false):
'AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111'
- add same entries again with once=exact, nothing should change
[] osmo_sdp_codec_list_add(AMR:octet-align=1;mode-set=0,2,4#112)
[] osmo_sdp_codec_list_add(GSM#3)
[] osmo_sdp_codec_list_add(GSM-HR-08#111)
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[1] = GSM#3
codec_list[2] = GSM-HR-08#111
list_ctx
| 9 test_codec_list
| 8 struct osmo_sdp_codec_list
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 2 struct osmo_sdp_codec
| 1 GSM
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
osmo_sdp_codec_list_to_str_c(summarize=true):
'AMR#112 GSM#3 GSM-HR-08#111'
osmo_sdp_codec_list_to_str_c(summarize=false):
'AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111'
- add same entries again with once=NULL, duplicates are added
[] osmo_sdp_codec_list_add(AMR:octet-align=1;mode-set=0,2,4#112)
[] osmo_sdp_codec_list_add(GSM#3)
[] osmo_sdp_codec_list_add(GSM-HR-08#111)
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[1] = GSM#3
codec_list[2] = GSM-HR-08#111
codec_list[3] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[4] = GSM#3
codec_list[5] = GSM-HR-08#111
list_ctx
| 16 test_codec_list
| 15 struct osmo_sdp_codec_list
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 2 struct osmo_sdp_codec
| 1 GSM
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 2 struct osmo_sdp_codec
| 1 GSM
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
osmo_sdp_codec_list_to_str_c(summarize=true):
'2*AMR#112 2*GSM#3 2*GSM-HR-08#111'
osmo_sdp_codec_list_to_str_c(summarize=false):
'AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111'
- add same entries again with once=NULL,pick_unused_pt_nr=true, duplicates are added with new #nr
[] osmo_sdp_codec_list_add(AMR:octet-align=1;mode-set=0,2,4#96)
[] osmo_sdp_codec_list_add(GSM#97)
[] osmo_sdp_codec_list_add(GSM-HR-08#98)
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[1] = GSM#3
codec_list[2] = GSM-HR-08#111
codec_list[3] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[4] = GSM#3
codec_list[5] = GSM-HR-08#111
codec_list[6] = AMR:octet-align=1;mode-set=0,2,4#96
codec_list[7] = GSM#97
codec_list[8] = GSM-HR-08#98
list_ctx
| 23 test_codec_list
| 22 struct osmo_sdp_codec_list
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 2 struct osmo_sdp_codec
| 1 GSM
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 2 struct osmo_sdp_codec
| 1 GSM
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 2 struct osmo_sdp_codec
| 1 GSM
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
osmo_sdp_codec_list_to_str_c(summarize=true):
'3*AMR 3*GSM 3*GSM-HR-08'
osmo_sdp_codec_list_to_str_c(summarize=false):
'AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98'
- remove all 'GSM#3' entries, with osmo_sdp_codec_cmp_exact
osmo_sdp_codec_list_remove() = 2
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[1] = GSM-HR-08#111
codec_list[2] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[3] = GSM-HR-08#111
codec_list[4] = AMR:octet-align=1;mode-set=0,2,4#96
codec_list[5] = GSM#97
codec_list[6] = GSM-HR-08#98
list_ctx
| 19 test_codec_list
| 18 struct osmo_sdp_codec_list
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 2 struct osmo_sdp_codec
| 1 GSM
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
osmo_sdp_codec_list_to_str_c(summarize=true):
'3*AMR 3*GSM-HR-08 GSM#97'
osmo_sdp_codec_list_to_str_c(summarize=false):
'AMR:octet-align=1;mode-set=0,2,4#112 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98'
- remove all 'GSM' entries, with osmo_sdp_codec_cmp_equivalent
osmo_sdp_codec_list_remove() = 1
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[1] = GSM-HR-08#111
codec_list[2] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[3] = GSM-HR-08#111
codec_list[4] = AMR:octet-align=1;mode-set=0,2,4#96
codec_list[5] = GSM-HR-08#98
list_ctx
| 17 test_codec_list
| 16 struct osmo_sdp_codec_list
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
osmo_sdp_codec_list_to_str_c(summarize=true):
'3*AMR 3*GSM-HR-08'
osmo_sdp_codec_list_to_str_c(summarize=false):
'AMR:octet-align=1;mode-set=0,2,4#112 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#96 GSM-HR-08#98'
- osmo_sdp_codec_list_move_to_first('AMR:octet-align=1;mode-set=0,2,4#112', equivalent) = 3
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[1] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[2] = AMR:octet-align=1;mode-set=0,2,4#96
codec_list[3] = GSM-HR-08#111
codec_list[4] = GSM-HR-08#111
codec_list[5] = GSM-HR-08#98
list_ctx
| 17 test_codec_list
| 16 struct osmo_sdp_codec_list
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
osmo_sdp_codec_list_to_str_c(summarize=true):
'3*AMR 3*GSM-HR-08'
osmo_sdp_codec_list_to_str_c(summarize=false):
'AMR:octet-align=1;mode-set=0,2,4#112 AMR:octet-align=1;mode-set=0,2,4#112 AMR:octet-align=1;mode-set=0,2,4#96 GSM-HR-08#111 GSM-HR-08#111 GSM-HR-08#98'
- osmo_sdp_codec_list_free_items()
0 entries
list_ctx
| 2 test_codec_list
| 1 struct osmo_sdp_codec_list
osmo_sdp_codec_list_to_str_c(summarize=true):
'(no-codecs)'
osmo_sdp_codec_list_to_str_c(summarize=false):
'(no-codecs)'
--- test_codec_list_cmp()
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
cmpf[1]: payload_type=false rate=true fmtp=1: A == B == A
cmpf[2]: payload_type=true rate=true fmtp=2: A == B == A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:octet-align=1;mode-set=0,2,4#96 GSM#3 GSM-HR-08#111
cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
cmpf[1]: payload_type=false rate=true fmtp=1: A == B == A
cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:mode-set=0,2,4;octet-align=1#112 GSM#3 GSM-HR-08#111
cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
cmpf[1]: payload_type=false rate=true fmtp=1: A == B == A
cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:octet-align=1;mode-set=7#112 GSM#3 GSM-HR-08#111
cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
cmpf[1]: payload_type=false rate=true fmtp=1: A < B > A
cmpf[2]: payload_type=true rate=true fmtp=2: A < B > A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:octet-align=1#112 GSM#3 GSM-HR-08#111
cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
cmpf[1]: payload_type=false rate=true fmtp=1: A == B == A
cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:octet-align=0;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
cmpf[1]: payload_type=false rate=true fmtp=1: A > B < A
cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
cmpf[1]: payload_type=false rate=true fmtp=1: A > B < A
cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = GSM#3 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112
cmpf[0]: payload_type=false rate=false fmtp=0: A < B > A
cmpf[1]: payload_type=false rate=true fmtp=1: A < B > A
cmpf[2]: payload_type=true rate=true fmtp=2: A < B > A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3
cmpf[0]: payload_type=false rate=false fmtp=0: A > B < A
cmpf[1]: payload_type=false rate=true fmtp=1: A > B < A
cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111 GSM-EFR#110
cmpf[0]: payload_type=false rate=false fmtp=0: A < B > A
cmpf[1]: payload_type=false rate=true fmtp=1: A < B > A
cmpf[2]: payload_type=true rate=true fmtp=2: A < B > A
--- test_codec_list_intersection()
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=false)
= AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=true)
= AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=false)
= AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=true)
= AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = GSM-HR-08#98 AMR:octet-align=1;mode-set=0,2,4#96 GSM#97
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=false)
= AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=true)
= AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=false)
= GSM-HR-08#98 AMR:octet-align=1;mode-set=0,2,4#96 GSM#97
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=true)
= GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112 GSM#3
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = GSM-HR-08#98 GSM-EFR#110 GSM#97
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=false)
= GSM#3 GSM-HR-08#111
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=true)
= GSM#97 GSM-HR-08#98
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=false)
= GSM-HR-08#98 GSM#97
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=true)
= GSM-HR-08#111 GSM#3
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR-WB/16000#97 FOO#98 GSM-EFR#110
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=false)
= (no-codecs)
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=true)
= (no-codecs)
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=false)
= (no-codecs)
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=true)
= (no-codecs)
--- test_obj_members()
o->codec = AMR:octet-align=1#96
ctx
| 6 test_obj_members
| 5 struct my_obj
| 3 struct osmo_sdp_codec
| 1 octet-align=1
| 1 AMR
| 1 struct osmo_sdp_codec_list
o->codec_list[0] = AMR:octet-align=1#96
o->codec_list[1] = AMR:octet-align=1#97
ctx
| 12 test_obj_members
| 11 struct my_obj
| 3 struct osmo_sdp_codec
| 1 octet-align=1
| 1 AMR
| 7 struct osmo_sdp_codec_list
| 3 struct osmo_sdp_codec
| 1 octet-align=1
| 1 AMR
| 3 struct osmo_sdp_codec
| 1 octet-align=1
| 1 AMR
talloc_free(o)
ctx
| 1 test_obj_members

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

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

View File

View File

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

764
tests/sdp/sdp_msg_test.c Normal file
View 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;
}

View File

1316
tests/sdp/sdp_msg_test.ok Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -13,3 +13,24 @@ AT_KEYWORDS([mgcp])
cat $abs_srcdir/mgcp/mgcp_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/mgcp/mgcp_test], [], [expout], [ignore])
AT_CLEANUP
AT_SETUP([sdp_fmtp])
AT_KEYWORDS([sdp_fmtp])
cat $abs_srcdir/sdp/sdp_fmtp_test.ok > expout
cat $abs_srcdir/sdp/sdp_fmtp_test.err > experr
AT_CHECK([$abs_top_builddir/tests/sdp/sdp_fmtp_test], [], [expout], [experr])
AT_CLEANUP
AT_SETUP([sdp_codec])
AT_KEYWORDS([sdp_codec])
cat $abs_srcdir/sdp/sdp_codec_test.ok > expout
cat $abs_srcdir/sdp/sdp_codec_test.err > experr
AT_CHECK([$abs_top_builddir/tests/sdp/sdp_codec_test], [], [expout], [experr])
AT_CLEANUP
AT_SETUP([sdp_msg])
AT_KEYWORDS([sdp_msg])
cat $abs_srcdir/sdp/sdp_msg_test.ok > expout
cat $abs_srcdir/sdp/sdp_msg_test.err > experr
AT_CHECK([$abs_top_builddir/tests/sdp/sdp_msg_test], [], [expout], [experr])
AT_CLEANUP