mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
synced 2025-11-02 13:03:33 +00:00
Compare commits
79 Commits
neels/wip
...
neels/sdp2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c42f44d7e | ||
|
|
53877bfb8e | ||
|
|
aca4ad4cce | ||
|
|
46c3882f75 | ||
|
|
5f8bad4b21 | ||
|
|
35357e80ee | ||
|
|
6c09836faa | ||
|
|
c43d76dc4d | ||
|
|
0051a29df9 | ||
|
|
ef5f1d3d7b | ||
|
|
c74c3d02c8 | ||
|
|
7338fd833b | ||
|
|
fcf1864db3 | ||
|
|
9221b67f3b | ||
|
|
732d595a26 | ||
|
|
3bc9d53628 | ||
|
|
b5a4f45dbf | ||
|
|
e6bafbf3e3 | ||
|
|
5c7b128166 | ||
|
|
5469edc278 | ||
|
|
66952183a9 | ||
|
|
cc97f58d96 | ||
|
|
5d6931881a | ||
|
|
0f4be7699b | ||
|
|
546983cdbc | ||
|
|
c04de4e7bb | ||
|
|
d46cdd80de | ||
|
|
6676e63c89 | ||
|
|
1f99c3aaae | ||
|
|
e6ef4e7484 | ||
|
|
b0fa041a28 | ||
|
|
e17bf77bdd | ||
|
|
802b1fad61 | ||
|
|
8a8973eec2 | ||
|
|
17e7ea66e4 | ||
|
|
9c3fad2c42 | ||
|
|
227a800d26 | ||
|
|
24cae67f3e | ||
|
|
0d47c408ac | ||
|
|
f1b557988d | ||
|
|
95e504aa97 | ||
|
|
a5ae091fdd | ||
|
|
249f799a88 | ||
|
|
dc395843ff | ||
|
|
c5d003e09e | ||
|
|
e6398d886b | ||
|
|
23140d6f94 | ||
|
|
36c823eef1 | ||
|
|
fa393fa1e9 | ||
|
|
e0fc37a437 | ||
|
|
4aa2b0e35b | ||
|
|
e01f6e7f33 | ||
|
|
388b48d14e | ||
|
|
352b967d1b | ||
|
|
ed4da25f2f | ||
|
|
5abda312ed | ||
|
|
75862d3131 | ||
|
|
8733542b13 | ||
|
|
d7aac20cc1 | ||
|
|
8b5361412c | ||
|
|
3a971ba0d1 | ||
|
|
c4b90354ca | ||
|
|
28fd236044 | ||
|
|
17b5701f19 | ||
|
|
9bea6eb78c | ||
|
|
fd57bd5f6f | ||
|
|
d0dbda4106 | ||
|
|
35a738214c | ||
|
|
afbe872045 | ||
|
|
909be19285 | ||
|
|
8ee08c6b78 | ||
|
|
c053e07336 | ||
|
|
2b30cbdfa8 | ||
|
|
c9df010416 | ||
|
|
3a3e1b57c6 | ||
|
|
186d4f1a0b | ||
|
|
cc2f7937ed | ||
|
|
960c031a67 | ||
|
|
4993452561 |
@@ -18,13 +18,14 @@ SUBDIRS = \
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = \
|
||||
libosmo-sdp.pc \
|
||||
libosmo-mgcp-client.pc \
|
||||
$(NULL)
|
||||
|
||||
BUILT_SOURCES = $(top_srcdir)/.version
|
||||
EXTRA_DIST = \
|
||||
.version \
|
||||
contrib/osmo-mgw.spec.in \
|
||||
README.md \
|
||||
debian \
|
||||
git-version-gen \
|
||||
osmoappdesc.py \
|
||||
|
||||
24
README
24
README
@@ -1,24 +0,0 @@
|
||||
About OsmoMGW
|
||||
=============
|
||||
|
||||
OsmoMGW originated from the OpenBSC project, which started as a minimalistic
|
||||
all-in-one implementation of the GSM Network. In 2017, OpenBSC had reached
|
||||
maturity and diversity (including M3UA SIGTRAN and 3G support in the form of
|
||||
IuCS and IuPS interfaces) that naturally lead to a separation of the all-in-one
|
||||
approach to fully independent separate programs as in typical GSM networks.
|
||||
|
||||
OsmoMGW was one of the parts split off from the old openbsc.git. It originated
|
||||
as a solution to merely navigate RTP streams through a NAT, but has since
|
||||
matured to a Media Gateway implementation that is capable of streaming RTP for
|
||||
2G (AoIP) and 3G (IuCS) GSM networks as well as (still not implemented at time
|
||||
of writing) transcoding between TRAU, various RTP payloads and IuUP.
|
||||
|
||||
The OsmoMGW program exposes an MGCP interface towards clients like OsmoMSC and
|
||||
OsmoBSC, and receives and sends RTP streams as configured via MGCP.
|
||||
|
||||
The libosmo-mgcp-client library exposes utilities used by e.g. OsmoMSC (found
|
||||
in osmo-msc.git) to instruct OsmoMGW via its MGCP service.
|
||||
|
||||
Find OsmoMGW issue tracker and wiki online at
|
||||
https://osmocom.org/projects/osmo-mgw
|
||||
https://osmocom.org/projects/osmo-mgw/wiki
|
||||
102
README.md
Normal file
102
README.md
Normal file
@@ -0,0 +1,102 @@
|
||||
osmo-mgw - Osmocom MGW (Media GateWay) Implementation
|
||||
=====================================================
|
||||
|
||||
This repository contains a C-language implementation of an MGW (Media
|
||||
GateWay) for use [not only] within the 2G (GSM) and/or 3G (UMTS)
|
||||
Cellular Network built using Osmocom CNI (Cellular Network
|
||||
Infrastructure) software.
|
||||
|
||||
The OsmoMGW program provides an MGCP interface towards an MGCP call agent
|
||||
(client) like OsmoMSC and OsmoBSC, and receives and sends RTP streams as
|
||||
configured via the MGCP control plane.
|
||||
|
||||
This Media Gateway implementation is capable of
|
||||
|
||||
* streaming RTP for 2G (3GPP AoIP and Abis-over-IP)
|
||||
* streaming RTP for 3G (IuCS including the IuFP protocol)
|
||||
* TDM (E1/T1) based Abis interface with TRAU frames on 16k sub-slots
|
||||
* basic support for LCLS (Local Call, Local Switch) related features
|
||||
* various built-in translation capabilities
|
||||
* between Abis TRAU frames and RTP formats
|
||||
* between 2G AMR/RTP and 3G AMR/IuFP/RTP
|
||||
* between bandwidth-efficient and octet-aligned AMR
|
||||
* between different standards for encapsulating GSM HR codec frames in RTP
|
||||
|
||||
osmo-mgw is typically co-located with
|
||||
|
||||
* osmo-bsc (GSM BSC)
|
||||
* osmo-msc (GSM/UMTS MSC)
|
||||
* osmo-hnbgw (UMTS HNBGW); osmo-mgw implements RTP relay between Iuh
|
||||
and IuCS interfaces
|
||||
|
||||
The libosmo-mgcp-client library exposes utilities used by e.g. OsmoMSC
|
||||
(found in osmo-msc.git) to instruct OsmoMGW via its MGCP service.
|
||||
|
||||
Homepage
|
||||
--------
|
||||
|
||||
You can find the OsmoMGW issue tracker and wiki online at
|
||||
<https://osmocom.org/projects/osmo-mgw> and <https://osmocom.org/projects/osmo-mgw/wiki>.
|
||||
|
||||
|
||||
GIT Repository
|
||||
--------------
|
||||
|
||||
You can clone from the official osmo-mgw.git repository using
|
||||
|
||||
git clone https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw
|
||||
|
||||
There is a web interface at <https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw>
|
||||
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
User Manuals and VTY reference manuals are [optionally] built in PDF form
|
||||
as part of the build process.
|
||||
|
||||
Pre-rendered PDF version of the current "master" can be found at
|
||||
[User Manual](https://ftp.osmocom.org/docs/latest/osmomgw-usermanual.pdf)
|
||||
as well as the [VTY Reference Manual](https://ftp.osmocom.org/docs/latest/osmomgw-vty-reference.pdf)
|
||||
|
||||
|
||||
Mailing List
|
||||
------------
|
||||
|
||||
Discussions related to osmo-mgw are happening on the
|
||||
openbsc@lists.osmocom.org mailing list, please see
|
||||
<https://lists.osmocom.org/mailman/listinfo/openbsc> for subscription
|
||||
options and the list archive.
|
||||
|
||||
Please observe the [Osmocom Mailing List
|
||||
Rules](https://osmocom.org/projects/cellular-infrastructure/wiki/Mailing_List_Rules)
|
||||
when posting.
|
||||
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
Our coding standards are described at
|
||||
<https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards>
|
||||
|
||||
We use a gerrit based patch submission/review process for managing
|
||||
contributions. Please see
|
||||
<https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit> for
|
||||
more details
|
||||
|
||||
The current patch queue for osmo-mgw can be seen at
|
||||
<https://gerrit.osmocom.org/#/q/project:osmo-mgw+status:open>
|
||||
|
||||
|
||||
History
|
||||
-------
|
||||
|
||||
OsmoMGW originated from the OpenBSC project, which started as a minimalistic
|
||||
all-in-one implementation of the GSM Network. In 2017, OpenBSC had reached
|
||||
maturity and diversity (including M3UA SIGTRAN and 3G support in the form of
|
||||
IuCS and IuPS interfaces) that naturally lead to a separation of the all-in-one
|
||||
approach to fully independent separate programs as in typical GSM networks.
|
||||
|
||||
OsmoMGW was one of the parts split off from the old openbsc.git. It originated
|
||||
as a solution to merely navigate RTP streams through a NAT, but has since
|
||||
matured.
|
||||
53
TODO-RELEASE
53
TODO-RELEASE
@@ -1,42 +1,11 @@
|
||||
# When cleaning up this file upon a release:
|
||||
#
|
||||
# - Note that the release version number is entirely unrelated to the API
|
||||
# versions. A release version 5.2.3 may happily have an API version of 42:7:5.
|
||||
#
|
||||
# - Bump API version in src/lib*/Makefile.am files according to chapter
|
||||
# "Library interface versions" of the libtool documentation.
|
||||
# https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
||||
#
|
||||
# - Iff the 'current' API version has changed, rename debian/lib*.install
|
||||
#
|
||||
# API version bumping for the impatient:
|
||||
# LIBVERSION=c:r:a (current:revision_of_current:backwards_compat_age)
|
||||
# 5:2:4 means that
|
||||
# - this implements version 5 of the API;
|
||||
# - this is the 2nd (compatible) revision of API version 5;
|
||||
# - this is backwards compatible to all APIs since 4 versions ago,
|
||||
# i.e. callers that need API versions from 1 to 5 can use this.
|
||||
#
|
||||
# Bumping API versions recipe:
|
||||
# If the library source code has changed at all since the last update, r++;
|
||||
# If any interfaces have been added, removed, or changed since the last update, c++, r=0;
|
||||
# If any interfaces have been added since the last public release, a++;
|
||||
# If any interfaces have been removed or changed since the last public release, a=0.
|
||||
#
|
||||
#library what description / commit summary line
|
||||
libosmo-mgcp-client remove public API These public API items have not been called by any of our osmo-programs
|
||||
for many years. Any sane caller of libosmo-mgcp-client should use the
|
||||
higher level osmo_mgcpc_* API instead. Move these to a private header:
|
||||
struct mgcp_response_head
|
||||
struct mgcp_response
|
||||
struct mgcp_msg
|
||||
mgcp_response_parse_params()
|
||||
mgcp_client_tx()
|
||||
mgcp_client_cancel()
|
||||
mgcp_msg_gen()
|
||||
mgcp_msg_trans_id()
|
||||
libosmo-mgcp-client remove public API Since codecs[] has been deprecated in favor of ptmap[], there is no use
|
||||
for the following functions; all known callers have been within
|
||||
osmo-mgw.git:
|
||||
map_codec_to_pt()
|
||||
map_pt_to_codec()
|
||||
# When cleaning up this file: bump API version in corresponding Makefile.am and rename corresponding debian/lib*.install
|
||||
# according to https://osmocom.org/projects/cellular-infrastructure/wiki/Make_a_new_release
|
||||
# In short: https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
|
||||
# LIBVERSION=c:r:a
|
||||
# If the library source code has changed at all since the last update, then increment revision: c:r + 1:a.
|
||||
# If any interfaces have been added, removed, or changed since the last update: c + 1:0:a.
|
||||
# If any interfaces have been added since the last public release: c:r:a + 1.
|
||||
# If any interfaces have been removed or changed since the last public release: c:r:0.
|
||||
#library what description / commit summary line
|
||||
libosmocore bump_dep; workaround Bump libosmocore version dependency after I68328adb952ca8833ba047cb3b49ccc6f8a1f1b5
|
||||
has been merged to libosmocore.git; then remove my_msgb_copy_c wrapper function.
|
||||
|
||||
19
configure.ac
19
configure.ac
@@ -44,13 +44,13 @@ AC_SEARCH_LIBS([dlsym], [dl dld], [LIBRARY_DLSYM="$LIBS";LIBS=""])
|
||||
AC_SUBST(LIBRARY_DLSYM)
|
||||
|
||||
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.9.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.9.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.9.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.9.0)
|
||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.4.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.5.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.5.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.10.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.10.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.10.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.10.0)
|
||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.5.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.6.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.6.0)
|
||||
|
||||
CFLAGS="$CFLAGS -DBUILDING_LIBOSMOMGCPCLIENT -pthread"
|
||||
CPPFLAGS="$CPPFLAGS -DBUILDING_LIBOSMOMGCPCLIENT -pthread"
|
||||
@@ -187,11 +187,14 @@ AM_CONFIG_HEADER(bscconfig.h)
|
||||
|
||||
AC_OUTPUT(
|
||||
libosmo-mgcp-client.pc
|
||||
libosmo-sdp.pc
|
||||
include/Makefile
|
||||
include/osmocom/Makefile
|
||||
include/osmocom/sdp/Makefile
|
||||
include/osmocom/mgcp_client/Makefile
|
||||
include/osmocom/mgcp/Makefile
|
||||
src/Makefile
|
||||
src/libosmo-sdp/Makefile
|
||||
src/libosmo-mgcp-client/Makefile
|
||||
src/libosmo-mgcp/Makefile
|
||||
src/osmo-mgw/Makefile
|
||||
@@ -199,10 +202,10 @@ AC_OUTPUT(
|
||||
tests/atlocal
|
||||
tests/mgcp_client/Makefile
|
||||
tests/mgcp/Makefile
|
||||
tests/sdp/Makefile
|
||||
doc/Makefile
|
||||
doc/examples/Makefile
|
||||
doc/manuals/Makefile
|
||||
contrib/Makefile
|
||||
contrib/systemd/Makefile
|
||||
contrib/osmo-mgw.spec
|
||||
Makefile)
|
||||
|
||||
@@ -22,7 +22,7 @@ export deps inst
|
||||
osmo-clean-workspace.sh
|
||||
|
||||
mkdir "$deps" || true
|
||||
osmo-build-dep.sh libosmocore "" ac_cv_path_DOXYGEN=false
|
||||
osmo-build-dep.sh libosmocore "" --disable-doxygen
|
||||
|
||||
verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]")
|
||||
|
||||
@@ -30,8 +30,8 @@ export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||
export LD_LIBRARY_PATH="$inst/lib"
|
||||
export PATH="$inst/bin:$PATH"
|
||||
|
||||
osmo-build-dep.sh libosmo-netif "" --disable-doxygen
|
||||
osmo-build-dep.sh libosmo-abis
|
||||
osmo-build-dep.sh libosmo-netif
|
||||
|
||||
# Additional configure options and depends
|
||||
CONFIG=""
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
#
|
||||
# spec file for package osmo-mgw
|
||||
#
|
||||
# Copyright (c) 2017, Martin Hauke <mardnh@gmx.de>
|
||||
#
|
||||
# All modifications and additions to the file contributed by third parties
|
||||
# remain the property of their copyright owners, unless otherwise agreed
|
||||
# upon. The license for this file, and modifications and additions to the
|
||||
# file, is the same license as for the pristine package itself (unless the
|
||||
# license for the pristine package is not an Open Source License, in which
|
||||
# case the license is the MIT License). An "Open Source License" is a
|
||||
# license that conforms to the Open Source Definition (Version 1.9)
|
||||
# published by the Open Source Initiative.
|
||||
|
||||
# Please submit bugfixes or comments via http://bugs.opensuse.org/
|
||||
#
|
||||
|
||||
Name: osmo-mgw
|
||||
Version: @VERSION@
|
||||
Release: 0
|
||||
Summary: Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks
|
||||
License: AGPL-3.0-or-later AND GPL-2.0-or-later
|
||||
Group: Hardware/Mobile
|
||||
URL: https://osmocom.org/projects/osmo-mgw
|
||||
Source: %{name}-%{version}.tar.xz
|
||||
BuildRequires: automake >= 1.9
|
||||
BuildRequires: libtool >= 2
|
||||
BuildRequires: pkgconfig >= 0.20
|
||||
%if 0%{?suse_version}
|
||||
BuildRequires: systemd-rpm-macros
|
||||
%endif
|
||||
BuildRequires: pkgconfig(libosmo-netif) >= 1.4.0
|
||||
BuildRequires: pkgconfig(libosmocore) >= 1.9.0
|
||||
BuildRequires: pkgconfig(libosmoctrl) >= 1.9.0
|
||||
BuildRequires: pkgconfig(libosmogsm) >= 1.9.0
|
||||
BuildRequires: pkgconfig(libosmovty) >= 1.9.0
|
||||
BuildRequires: pkgconfig(libosmocoding) >= 1.9.0
|
||||
BuildRequires: pkgconfig(libosmoabis) >= 1.5.0
|
||||
BuildRequires: pkgconfig(libosmotrau) >= 1.5.0
|
||||
%{?systemd_requires}
|
||||
|
||||
%description
|
||||
OsmoMGW is Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks.
|
||||
|
||||
%package -n libosmo-mgcp-client12
|
||||
Summary: Osmocom's Media Gateway Control Protocol client library
|
||||
Group: System/Libraries
|
||||
|
||||
%description -n libosmo-mgcp-client12
|
||||
Osmocom's Media Gateway Control Protocol client library.
|
||||
|
||||
%package -n libosmo-mgcp-client-devel
|
||||
Summary: Development files for Osmocom's Media Gateway Control Protocol client library
|
||||
Group: Development/Libraries/C and C++
|
||||
Requires: libosmo-mgcp-client12 = %{version}
|
||||
|
||||
%description -n libosmo-mgcp-client-devel
|
||||
Osmocom's Media Gateway Control Protocol client librarary.
|
||||
|
||||
This subpackage contains libraries and header files for developing
|
||||
applications that want to make use of libosmo-mgcp-client.
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
%build
|
||||
echo "%{version}" >.tarball-version
|
||||
autoreconf -fi
|
||||
%configure \
|
||||
--disable-static \
|
||||
--docdir=%{_docdir}/%{name} \
|
||||
--with-systemdsystemunitdir=%{_unitdir}
|
||||
|
||||
make %{?_smp_mflags}
|
||||
|
||||
%install
|
||||
%make_install
|
||||
find %{buildroot} -type f -name "*.la" -delete -print
|
||||
|
||||
%check
|
||||
make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
|
||||
|
||||
%post -n libosmo-mgcp-client12 -p /sbin/ldconfig
|
||||
%postun -n libosmo-mgcp-client12 -p /sbin/ldconfig
|
||||
|
||||
%if 0%{?suse_version}
|
||||
%preun
|
||||
%service_del_preun osmo-mgw.service
|
||||
|
||||
%postun
|
||||
%service_del_postun osmo-mgw.service
|
||||
|
||||
%pre
|
||||
%service_add_pre osmo-mgw.service
|
||||
|
||||
%post
|
||||
%service_add_post osmo-mgw.service
|
||||
%endif
|
||||
|
||||
%files
|
||||
%license COPYING
|
||||
%doc AUTHORS README
|
||||
%dir %{_docdir}/%{name}/examples
|
||||
%dir %{_docdir}/%{name}/examples/osmo-mgw
|
||||
%{_docdir}/%{name}/examples/osmo-mgw/osmo-mgw.cfg
|
||||
%{_docdir}/%{name}/examples/osmo-mgw/osmo-mgw-abis_e1.cfg
|
||||
%{_bindir}/osmo-mgw
|
||||
%{_unitdir}/osmo-mgw.service
|
||||
%dir %{_sysconfdir}/osmocom
|
||||
%config(noreplace) %{_sysconfdir}/osmocom/osmo-mgw.cfg
|
||||
|
||||
%files -n libosmo-mgcp-client12
|
||||
%{_libdir}/libosmo-mgcp-client.so.12*
|
||||
|
||||
%files -n libosmo-mgcp-client-devel
|
||||
%{_libdir}/libosmo-mgcp-client.so
|
||||
%{_libdir}/pkgconfig/libosmo-mgcp-client.pc
|
||||
%dir %{_includedir}/osmocom
|
||||
%dir %{_includedir}/osmocom/mgcp_client
|
||||
%{_includedir}/osmocom/mgcp_client/*.h
|
||||
|
||||
%changelog
|
||||
@@ -9,8 +9,11 @@ LimitNOFILE=65536
|
||||
StateDirectory=osmocom
|
||||
WorkingDirectory=%S/osmocom
|
||||
Restart=always
|
||||
User=osmocom
|
||||
Group=osmocom
|
||||
ExecStart=/usr/bin/osmo-mgw -s -c /etc/osmocom/osmo-mgw.cfg
|
||||
RestartSec=2
|
||||
AmbientCapabilities=CAP_SYS_NICE
|
||||
# CPU scheduling policy:
|
||||
CPUSchedulingPolicy=rr
|
||||
# For real-time scheduling policies an integer between 1 (lowest priority) and 99 (highest priority):
|
||||
|
||||
91
debian/changelog
vendored
91
debian/changelog
vendored
@@ -1,3 +1,94 @@
|
||||
osmo-mgw (1.13.1) unstable; urgency=medium
|
||||
|
||||
[ Philipp Maier ]
|
||||
* mgcp_network: use an uint16_t to store the port number
|
||||
* mgcp_network: add missing ntohs
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* tests/mgcp/mgcp_test: Add some extra asserts in code
|
||||
|
||||
-- Oliver Smith <osmith@sysmocom.de> Thu, 12 Sep 2024 13:53:19 +0200
|
||||
|
||||
osmo-mgw (1.13.0) unstable; urgency=medium
|
||||
|
||||
[ Neels Hofmeyr ]
|
||||
* add mgcp_conn_rtp_type_names[]
|
||||
* mgcp_parse_audio_port_pt(): fix buffer overflow
|
||||
* client: replace two assertions with graceful error handling
|
||||
* systemd,manual: set LimitNOFILE=65536
|
||||
* IuUP: allow Initialization from any address if not yet set
|
||||
* check_rtp_origin: drop special case for legacy IuUP hack
|
||||
* mgcp_client_test: fix function name
|
||||
* fix possible NULL deref on early media
|
||||
* client: move some items to internal header
|
||||
* client: safely handle dealloc on event dispatch
|
||||
* build: move mgcp/*.h to noinst_HEADERS, drop RPM libosmo-mgcp-devel
|
||||
* client: deprecate legacy API
|
||||
* client: collapse codecs[] and ptmap[]; allow codec variants
|
||||
* client: allow MGCP_MAX_CODECS entries
|
||||
* client SDP: more verbose error logging
|
||||
* mgcp_client_test: add test_parse_response()
|
||||
* drop (now) unused code
|
||||
* tests/mgcp: add update_exp target
|
||||
* drop get_net_downlink_format_cb
|
||||
* drop cfg 'sdp audio fmtp-extra'
|
||||
* mgcp_codec_decide: remove redundant lookup
|
||||
* tweak DEBUG log
|
||||
* mgcp_test: fix false negatives in test output
|
||||
* mgw: do not fail MGCP on codec mismatch
|
||||
* mgcp-client: always send 'm=audio' line
|
||||
* mgcp_test.c: verify osmo-mgw accepts m=audio 0
|
||||
* mgcp_test.c: fix various missing '\r' and '\n'
|
||||
* mgcp_test: test a=ptime:20, not 40
|
||||
* mgcp_test: add CRCX for IUFP in sendrecv
|
||||
* do not FAIL on CRCX in sendrecv mode
|
||||
|
||||
[ Keith Whyte ]
|
||||
* vty and log: also show local port for RTP conns
|
||||
|
||||
[ Andreas Eversberg ]
|
||||
* Use uniform log format for default config files
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* mgcp_network: Improve err logging when rtp pkt from unexpected origin comes in
|
||||
* cosmetic: Fix line indentation
|
||||
* IuUP: Allow Initialization with set rem IP address and unset rem port
|
||||
* mgcp-client: Transmit remote IP addr in CRCX if known and port=0
|
||||
* Fix IuUP RTP hdr seqnr field not incremented
|
||||
* iuup: Increment RTP hdr seqnr even if Tx over UDP fails
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* mgcp: simplify getting msgb tail in mgcp_msg_terminate_nul()
|
||||
* mgcp: reserve once byte for '\0' in mgcp_do_read()
|
||||
* mgcp: correctly put NUL character in mgcp_msg_terminate_nul()
|
||||
* build: include README into the release tarball
|
||||
|
||||
[ neels ]
|
||||
* Revert "drop (now) unused code"
|
||||
|
||||
[ Harald Welte ]
|
||||
* Convert README to README.md and expand like in other projects
|
||||
* migrate mgcp_client from osmo_wqueue to osmo_io
|
||||
* mgw: Add our usual SIGABRT, SIGUSR1 signal handlers
|
||||
* don't log useless "transcoding disabled" message
|
||||
* remove strange loop for non-existant transcoding support
|
||||
* simplify unused transcoding/processing call-back
|
||||
* remove osmo_fd from mgcp_create_bind()
|
||||
* Change msgb ownership in processing of received msgb
|
||||
* cosmetic: make linter happy
|
||||
* Convert RTP/RTCP/OSMUX I/O from osmo_fd to osmo_io
|
||||
|
||||
[ Oliver Smith ]
|
||||
* contrib: remove rpm spec file
|
||||
* contrib/systemd: run as osmocom user
|
||||
* doc: example configs: fix deprecation warnings
|
||||
|
||||
[ Mychaela N. Falconia ]
|
||||
* E1: support HRv1 codec on both 16k and 8k subslots
|
||||
* fix E1 TS output when used with osmo-e1d
|
||||
|
||||
-- Oliver Smith <osmith@sysmocom.de> Wed, 24 Jul 2024 15:44:47 +0200
|
||||
|
||||
osmo-mgw (1.12.1) unstable; urgency=medium
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
|
||||
27
debian/control
vendored
27
debian/control
vendored
@@ -6,10 +6,10 @@ Build-Depends: debhelper (>= 10),
|
||||
dh-autoreconf,
|
||||
pkg-config,
|
||||
autotools-dev,
|
||||
libosmocore-dev (>= 1.9.0),
|
||||
libosmo-netif-dev (>= 1.4.0),
|
||||
libosmo-abis-dev (>= 1.5.0),
|
||||
osmo-gsm-manuals-dev (>= 1.5.0)
|
||||
libosmocore-dev (>= 1.10.0),
|
||||
libosmo-netif-dev (>= 1.5.0),
|
||||
libosmo-abis-dev (>= 1.6.0),
|
||||
osmo-gsm-manuals-dev (>= 1.6.0)
|
||||
Standards-Version: 3.9.8
|
||||
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw
|
||||
Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw
|
||||
@@ -21,7 +21,7 @@ Multi-Arch: foreign
|
||||
Depends: ${misc:Depends}, ${shlibs:Depends}
|
||||
Description: OsmoMGW: Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks
|
||||
|
||||
Package: libosmo-mgcp-client12
|
||||
Package: libosmo-mgcp-client14
|
||||
Section: libs
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
@@ -33,9 +33,24 @@ Package: libosmo-mgcp-client-dev
|
||||
Section: libdevel
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Depends: libosmo-mgcp-client12 (= ${binary:Version}), ${misc:Depends}
|
||||
Depends: libosmo-mgcp-client14 (= ${binary:Version}), ${misc:Depends}
|
||||
Description: libosmo-mgcp-client: Osmocom's Media Gateway Control Protocol client utilities
|
||||
|
||||
Package: libosmo-sdp1
|
||||
Section: libs
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Pre-Depends: ${misc:Pre-Depends}
|
||||
Depends: ${misc:Depends}, ${shlibs:Depends}
|
||||
Description: libosmo-sdp: Osmocom's Session Description Protocol encoder and decoder
|
||||
|
||||
Package: libosmo-sdp-dev
|
||||
Section: libdevel
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Depends: libosmo-sdp1 (= ${binary:Version}), ${misc:Depends}
|
||||
Description: libosmo-sdp: Osmocom's Session Description Protocol encoder and decoder
|
||||
|
||||
Package: osmo-mgw-doc
|
||||
Architecture: all
|
||||
Section: doc
|
||||
|
||||
19
debian/copyright
vendored
19
debian/copyright
vendored
@@ -63,3 +63,22 @@ License: GPL-3.0+
|
||||
Files: osmoappdesc.py
|
||||
Copyright: 2013 Katerina Barone-Adesi <kat.obsc@gmail.com>
|
||||
License: GPL-3.0+
|
||||
|
||||
Files: src/libosmo-sdp/* include/osmocom/sdp/*
|
||||
Copyright: 2024 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
License: GPL-3.0+
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
.
|
||||
On Debian systems, the complete text of the GNU General Public License
|
||||
Version 3 can be found in `/usr/share/common-licenses/GPL-3'.
|
||||
|
||||
4
debian/libosmo-sdp-dev.install
vendored
Normal file
4
debian/libosmo-sdp-dev.install
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
usr/include/osmocom/sdp
|
||||
usr/lib/*/libosmo-sdp.so
|
||||
usr/lib/*/libosmo-sdp.a
|
||||
usr/lib/*/pkgconfig/libosmo-sdp.pc
|
||||
1
debian/libosmo-sdp1.install
vendored
Normal file
1
debian/libosmo-sdp1.install
vendored
Normal file
@@ -0,0 +1 @@
|
||||
usr/lib/*/libosmo-sdp.so.*
|
||||
38
debian/postinst
vendored
Executable file
38
debian/postinst
vendored
Executable file
@@ -0,0 +1,38 @@
|
||||
#!/bin/sh -e
|
||||
case "$1" in
|
||||
configure)
|
||||
# Create the osmocom group and user (if it doesn't exist yet)
|
||||
if ! getent group osmocom >/dev/null; then
|
||||
groupadd --system osmocom
|
||||
fi
|
||||
if ! getent passwd osmocom >/dev/null; then
|
||||
useradd \
|
||||
--system \
|
||||
--gid osmocom \
|
||||
--home-dir /var/lib/osmocom \
|
||||
--shell /sbin/nologin \
|
||||
--comment "Open Source Mobile Communications" \
|
||||
osmocom
|
||||
fi
|
||||
|
||||
# Fix permissions of previous (root-owned) install (OS#4107)
|
||||
if dpkg --compare-versions "$2" le "1.13.0"; then
|
||||
if [ -e /etc/osmocom/osmo-mgw.cfg ]; then
|
||||
chown -v osmocom:osmocom /etc/osmocom/osmo-mgw.cfg
|
||||
chmod -v 0660 /etc/osmocom/osmo-mgw.cfg
|
||||
fi
|
||||
|
||||
if [ -d /etc/osmocom ]; then
|
||||
chown -v root:osmocom /etc/osmocom
|
||||
chmod -v 2775 /etc/osmocom
|
||||
fi
|
||||
|
||||
mkdir -p /var/lib/osmocom
|
||||
chown -R -v osmocom:osmocom /var/lib/osmocom
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# dh_installdeb(1) will replace this with shell code automatically
|
||||
# generated by other debhelper scripts.
|
||||
#DEBHELPER#
|
||||
@@ -14,15 +14,12 @@ e1_input
|
||||
e1_line 0 port 0
|
||||
mgcp
|
||||
bind ip 127.0.0.1
|
||||
rtp port-range 4002 16000
|
||||
rtp port-range 4002 16001
|
||||
rtp bind-ip 127.0.0.1
|
||||
rtp ip-probing
|
||||
rtp ip-dscp 46
|
||||
bind port 2427
|
||||
sdp audio payload number 98
|
||||
sdp audio payload name GSM
|
||||
number endpoints 512
|
||||
loop 0
|
||||
force-realloc 1
|
||||
rtcp-omit
|
||||
rtp-patch ssrc
|
||||
|
||||
@@ -11,15 +11,12 @@ log stderr
|
||||
|
||||
mgcp
|
||||
bind ip 127.0.0.1
|
||||
rtp port-range 4002 16000
|
||||
rtp port-range 4002 16001
|
||||
rtp bind-ip 127.0.0.1
|
||||
rtp ip-probing
|
||||
rtp ip-dscp 46
|
||||
bind port 2427
|
||||
sdp audio payload number 98
|
||||
sdp audio payload name GSM
|
||||
number endpoints 512
|
||||
loop 0
|
||||
force-realloc 1
|
||||
rtcp-omit
|
||||
rtp-patch ssrc
|
||||
|
||||
@@ -8,7 +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/mgcp_client/fmtp.h \
|
||||
osmocom/sdp/fmtp.h \
|
||||
osmocom/sdp/sdp_codec.h \
|
||||
osmocom/sdp/sdp_codec_list.h \
|
||||
osmocom/sdp/sdp_msg.h \
|
||||
osmocom/sdp/sdp_strings.h \
|
||||
$(NULL)
|
||||
|
||||
noinst_HEADERS = \
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
SUBDIRS = \
|
||||
sdp \
|
||||
mgcp_client \
|
||||
mgcp \
|
||||
$(NULL)
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/osmo_io.h>
|
||||
#include <osmocom/core/write_queue.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
@@ -70,12 +71,10 @@ typedef int (*mgcp_rqnt)(struct mgcp_endpoint *endp, char tone);
|
||||
/**
|
||||
* Return:
|
||||
* < 0 in case no audio was processed
|
||||
* >= 0 in case audio was processed. The remaining payload
|
||||
* length will be returned.
|
||||
* >= 0 in case audio was processed.
|
||||
*/
|
||||
typedef int (*mgcp_processing)(struct mgcp_endpoint *endp,
|
||||
struct mgcp_rtp_end *dst_end,
|
||||
char *data, int *len, int buf_size);
|
||||
struct mgcp_rtp_end *dst_end, struct msgb *msg);
|
||||
|
||||
struct mgcp_conn_rtp;
|
||||
|
||||
@@ -206,6 +205,5 @@ int mgcp_send_reset_ep(struct mgcp_endpoint *endp);
|
||||
int mgcp_send_reset_all(struct mgcp_config *cfg);
|
||||
|
||||
|
||||
int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port, uint8_t dscp,
|
||||
uint8_t prio);
|
||||
int mgcp_udp_send(int fd, const struct osmo_sockaddr *addr, const char *buf, int len);
|
||||
int mgcp_create_bind(const char *source_addr, int port, uint8_t dscp, uint8_t prio);
|
||||
int mgcp_udp_send(struct osmo_io_fd *iofd, const struct osmo_sockaddr *addr, const char *buf, int len);
|
||||
|
||||
@@ -13,7 +13,6 @@ struct mgcp_conn_rtp;
|
||||
void mgcp_codec_summary(struct mgcp_conn_rtp *conn);
|
||||
void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn);
|
||||
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param);
|
||||
int mgcp_codec_add2(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const char *fmtp);
|
||||
int mgcp_codec_decide(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst);
|
||||
const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn_rtp *conn,
|
||||
const char *subtype_name, unsigned int match_nr);
|
||||
|
||||
@@ -59,7 +59,7 @@ enum mgcp_x_osmo_ign {
|
||||
MGCP_X_OSMO_IGN_CALLID = 1,
|
||||
};
|
||||
|
||||
/* Deprecated. Use the new fmtp string instead. */
|
||||
/* Codec parameters (communicated via SDP/fmtp) */
|
||||
struct mgcp_codec_param {
|
||||
bool amr_octet_aligned_present;
|
||||
bool amr_octet_aligned;
|
||||
|
||||
@@ -24,10 +24,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
#include <osmocom/mgcp/mgcp_common.h>
|
||||
#include <osmocom/mgcp/mgcp_network.h>
|
||||
#include <osmocom/mgcp/osmux.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/gsm/iuup.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
@@ -237,15 +239,16 @@ static inline bool mgcp_conn_rtp_is_iuup(const struct mgcp_conn_rtp *conn)
|
||||
return conn->type == MGCP_RTP_IUUP;
|
||||
}
|
||||
|
||||
static inline struct mgcp_conn_rtp *mgcp_conn_get_conn_rtp(struct mgcp_conn *conn)
|
||||
{
|
||||
OSMO_ASSERT(conn->type == MGCP_CONN_TYPE_RTP);
|
||||
return &conn->u.rtp;
|
||||
}
|
||||
|
||||
struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
|
||||
enum mgcp_conn_type type, char *name);
|
||||
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id);
|
||||
struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp,
|
||||
const char *id);
|
||||
void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id);
|
||||
void mgcp_conn_free_oldest(struct mgcp_endpoint *endp);
|
||||
void mgcp_conn_free_all(struct mgcp_endpoint *endp);
|
||||
void mgcp_conn_free(struct mgcp_conn *conn);
|
||||
int mgcp_conn_set_mode(struct mgcp_conn *conn, enum mgcp_connection_mode mode);
|
||||
char *mgcp_conn_dump(struct mgcp_conn *conn);
|
||||
struct mgcp_conn *mgcp_find_dst_conn(struct mgcp_conn *conn);
|
||||
struct mgcp_conn *mgcp_conn_get_oldest(struct mgcp_endpoint *endp);
|
||||
void mgcp_conn_watchdog_kick(struct mgcp_conn *conn);
|
||||
|
||||
@@ -138,11 +138,20 @@ struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname,
|
||||
const struct mgcp_trunk *trunk);
|
||||
struct mgcp_endpoint *mgcp_endp_by_name(int *cause, const char *epname,
|
||||
struct mgcp_config *cfg);
|
||||
bool mgcp_endp_avail(struct mgcp_endpoint *endp);
|
||||
bool mgcp_endp_avail(const struct mgcp_endpoint *endp);
|
||||
unsigned int mgcp_endp_num_conns(const struct mgcp_endpoint *endp);
|
||||
bool mgcp_endp_is_full(const struct mgcp_endpoint *endp);
|
||||
void mgcp_endp_add_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
|
||||
void mgcp_endp_remove_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
|
||||
void mgcp_endp_free_conn_oldest(struct mgcp_endpoint *endp);
|
||||
void mgcp_endp_free_conn_all(struct mgcp_endpoint *endp);
|
||||
void mgcp_endp_strip_name(char *epname_stripped, const char *epname,
|
||||
const struct mgcp_trunk *trunk);
|
||||
struct mgcp_endpoint *mgcp_endp_find_specific(const char *epname,
|
||||
const struct mgcp_trunk *trunk);
|
||||
void mgcp_endp_release(struct mgcp_endpoint *endp);
|
||||
|
||||
struct mgcp_conn *mgcp_endp_get_conn(struct mgcp_endpoint *endp, const char *id);
|
||||
struct mgcp_conn *mgcp_endp_get_conn_oldest(struct mgcp_endpoint *endp);
|
||||
struct mgcp_conn_rtp *mgcp_endp_get_conn_rtp(struct mgcp_endpoint *endp,
|
||||
const char *id);
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <osmocom/mgcp/mgcp_common.h>
|
||||
|
||||
struct mgcp_conn;
|
||||
struct mgcp_parse_data;
|
||||
struct mgcp_endpoint;
|
||||
@@ -34,8 +36,7 @@ struct mgcp_trunk;
|
||||
|
||||
void mgcp_disp_msg(unsigned char *message, unsigned int len, char *preamble);
|
||||
|
||||
int mgcp_parse_conn_mode(const char *msg, struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn *conn);
|
||||
enum mgcp_connection_mode mgcp_parse_conn_mode(const char *msg);
|
||||
|
||||
int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/osmo_io.h>
|
||||
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
|
||||
@@ -83,11 +84,8 @@ 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 */
|
||||
@@ -96,7 +94,7 @@ struct mgcp_rtp_end {
|
||||
struct osmo_sockaddr addr;
|
||||
|
||||
/* in network byte order */
|
||||
int rtcp_port;
|
||||
uint16_t rtcp_port;
|
||||
|
||||
/* currently selected audio codec */
|
||||
struct mgcp_rtp_codec *codec;
|
||||
@@ -123,8 +121,8 @@ struct mgcp_rtp_end {
|
||||
bool rfc5993_hr_convert;
|
||||
|
||||
/* Each end has a separate socket for RTP and RTCP */
|
||||
struct osmo_fd rtp;
|
||||
struct osmo_fd rtcp;
|
||||
struct osmo_io_fd *rtp;
|
||||
struct osmo_io_fd *rtcp;
|
||||
|
||||
/* local UDP port number of the RTP socket; RTCP is +1 */
|
||||
int local_port;
|
||||
@@ -134,6 +132,7 @@ struct mgcp_rtp_end {
|
||||
};
|
||||
|
||||
bool mgcp_rtp_end_remote_addr_available(const struct mgcp_rtp_end *rtp_end);
|
||||
void mgcp_rtp_end_free_port(struct mgcp_rtp_end *end);
|
||||
|
||||
struct mgcp_rtp_tap {
|
||||
/* is this tap active (1) or not (0) */
|
||||
@@ -154,7 +153,6 @@ int mgcp_dispatch_e1_bridge_cb(struct msgb *msg);
|
||||
void mgcp_cleanup_e1_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
|
||||
int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
|
||||
struct mgcp_conn_rtp *conn);
|
||||
void mgcp_free_rtp_port(struct mgcp_rtp_end *end);
|
||||
void mgcp_patch_and_count(const struct mgcp_endpoint *endp,
|
||||
struct mgcp_rtp_state *state,
|
||||
struct mgcp_rtp_end *rtp_end,
|
||||
@@ -162,8 +160,7 @@ void mgcp_patch_and_count(const struct mgcp_endpoint *endp,
|
||||
int mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn);
|
||||
|
||||
/* payload processing default functions */
|
||||
int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end,
|
||||
char *data, int *len, int buf_size);
|
||||
int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end, struct msgb *msg);
|
||||
|
||||
int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn_rtp *conn_dst,
|
||||
@@ -183,7 +180,7 @@ void rtpconn_rate_ctr_add(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *
|
||||
int id, int inc);
|
||||
void rtpconn_rate_ctr_inc(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp,
|
||||
int id);
|
||||
void forward_data_tap(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg);
|
||||
void forward_data_tap(struct osmo_io_fd *iofd, struct mgcp_rtp_tap *tap, struct msgb *msg);
|
||||
uint32_t mgcp_get_current_ts(unsigned codec_rate);
|
||||
|
||||
int amr_oa_bwe_convert(struct mgcp_endpoint *endp, struct msgb *msg, bool target_is_oa);
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/mgcp/mgcp_common.h>
|
||||
|
||||
/* Internal structure while parsing a request */
|
||||
struct mgcp_parse_data {
|
||||
struct mgcp_config *cfg;
|
||||
@@ -28,3 +31,9 @@ void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
|
||||
|
||||
uint32_t mgcp_rtp_packet_duration(const struct mgcp_endpoint *endp,
|
||||
const struct mgcp_rtp_end *rtp);
|
||||
|
||||
extern const struct value_string mgcp_connection_mode_strs[];
|
||||
static inline const char *mgcp_cmode_name(enum mgcp_connection_mode mode)
|
||||
{
|
||||
return get_value_string(mgcp_connection_mode_strs, mode);
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define OSMO_SDP_NAME_A "a"
|
||||
#define OSMO_SDP_NAME_FMTP "fmtp"
|
||||
#define OSMO_SDP_NAME_AMR_OCTET_ALIGN "octet-align"
|
||||
|
||||
#define OSMO_SDP_VAL_AMR_OCTET_ALIGN_0 OSMO_SDP_NAME_AMR_OCTET_ALIGN "=0"
|
||||
#define OSMO_SDP_VAL_AMR_OCTET_ALIGN_1 OSMO_SDP_NAME_AMR_OCTET_ALIGN "=1"
|
||||
|
||||
/* "fmtp:" */
|
||||
#define OSMO_SDP_PREFIX_FMTP OSMO_SDP_NAME_FMTP ":"
|
||||
/* "a=fmtp:" */
|
||||
#define OSMO_SDP_PREFIX_A_FMTP OSMO_SDP_NAME_A "=" OSMO_SDP_PREFIX_FMTP
|
||||
|
||||
bool osmo_sdp_fmtp_get_val(char *val, size_t val_size, const char *fmtp, const char *option_name);
|
||||
int osmo_sdp_fmtp_get_int(const char *fmtp, const char *option_name, int default_value);
|
||||
|
||||
/* Some AMR related fmtp parameters as in https://www.rfc-editor.org/rfc/rfc4867#section-8.1 that osmo-mgw needs.*/
|
||||
bool osmo_sdp_fmtp_amr_is_octet_aligned(const char *fmtp);
|
||||
|
||||
/*! To compose AMR related fmtp indicating octet-align.
|
||||
* Usage:
|
||||
* printf("%s", OSMO_SDP_AMR_SET_OCTET_ALIGN(oa_flag));
|
||||
*/
|
||||
#define OSMO_SDP_AMR_SET_OCTET_ALIGN(VAL) \
|
||||
((VAL) ? OSMO_SDP_VAL_AMR_OCTET_ALIGN_1 : OSMO_SDP_VAL_AMR_OCTET_ALIGN_0 )
|
||||
@@ -77,9 +77,6 @@ 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);
|
||||
@@ -122,5 +119,9 @@ static inline const char *mgcp_client_cmode_name(enum mgcp_connection_mode mode)
|
||||
}
|
||||
|
||||
enum mgcp_codecs map_str_to_codec(const char *str);
|
||||
unsigned int map_codec_to_pt(const struct ptmap *ptmap, unsigned int ptmap_len,
|
||||
enum mgcp_codecs codec);
|
||||
enum mgcp_codecs map_pt_to_codec(struct ptmap *ptmap, unsigned int ptmap_len,
|
||||
unsigned int pt);
|
||||
|
||||
const char *mgcp_client_name(const struct mgcp_client *mgcp);
|
||||
|
||||
@@ -12,11 +12,7 @@
|
||||
* When modifiying a connection, the endpoint and call_id members may be left
|
||||
* unpopulated. The call_id field is ignored in this case. If an endpoint
|
||||
* identifier is supplied it is checked against the internal state to make
|
||||
* sure it is correct.
|
||||
*
|
||||
* CAUTION: This struct may be subject to changes and new struct members may
|
||||
* be added in the future. To prevent memory conflicts it is strongly advised
|
||||
* to allocate this struct dynamically using mgcp_conn_peer_alloc() */
|
||||
* sure it is correct. */
|
||||
struct mgcp_conn_peer {
|
||||
/*! RTP connection IP-Address (optional, string e.g. "127.0.0.1") */
|
||||
char addr[INET6_ADDRSTRLEN];
|
||||
@@ -61,15 +57,12 @@ struct mgcp_conn_peer {
|
||||
* address is set. If != MGCP_CONN_NONE, force this conn mode. */
|
||||
enum mgcp_connection_mode conn_mode;
|
||||
|
||||
/*! 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. */
|
||||
/*! 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;
|
||||
};
|
||||
|
||||
struct mgcp_conn_peer *mgcp_conn_peer_alloc(void *ctx);
|
||||
|
||||
struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm_inst *parent_fi, uint32_t parent_term_evt,
|
||||
uint32_t parent_evt, struct mgcp_conn_peer *conn_peer)
|
||||
OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use osmo_mgcpc_ep_alloc() and osmo_mgcpc_ep_ci_add() instead");
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/write_queue.h>
|
||||
#include <osmocom/core/osmo_io.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
|
||||
#include <osmocom/mgcp_client/mgcp_client.h>
|
||||
|
||||
#define MSGB_CB_MGCP_TRANS_ID 0
|
||||
|
||||
/* Struct that holds one endpoint name */
|
||||
@@ -13,7 +15,7 @@ struct reset_ep {
|
||||
|
||||
struct mgcp_client {
|
||||
struct mgcp_client_conf actual;
|
||||
struct osmo_wqueue wq;
|
||||
struct osmo_io_fd *iofd;
|
||||
mgcp_trans_id_t next_trans_id;
|
||||
struct llist_head responses_pending;
|
||||
struct mgcp_client_pool_member *pool_member;
|
||||
|
||||
3
include/osmocom/sdp/Makefile.am
Normal file
3
include/osmocom/sdp/Makefile.am
Normal file
@@ -0,0 +1,3 @@
|
||||
noinst_HEADERS = \
|
||||
sdp_internal.h \
|
||||
$(NULL)
|
||||
34
include/osmocom/sdp/fmtp.h
Normal file
34
include/osmocom/sdp/fmtp.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/* Public API for codec management in SDP messages: managing SDP fmtp strings. */
|
||||
/*
|
||||
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
bool osmo_sdp_fmtp_get_val(char *val, size_t val_size, const char *fmtp, const char *option_name);
|
||||
int64_t osmo_sdp_fmtp_get_int(const char *fmtp, const char *option_name, int64_t default_value);
|
||||
|
||||
bool osmo_sdp_fmtp_amr_is_octet_aligned(const char *fmtp);
|
||||
bool osmo_sdp_fmtp_amr_match(const char *a, const char *b);
|
||||
126
include/osmocom/sdp/sdp_codec.h
Normal file
126
include/osmocom/sdp/sdp_codec.h
Normal file
@@ -0,0 +1,126 @@
|
||||
/* Public API for codec management in SDP messages. */
|
||||
/*
|
||||
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
|
||||
/* RFC-8866 5.14 and 6.6.
|
||||
*
|
||||
* Represent the items describing an SDP codec entry, as in:
|
||||
*
|
||||
* m=audio 1234 RTP/AVP <payload_type>
|
||||
* a=rtpmap:<payload_type> <encoding-name>/<clock-rate>
|
||||
* a=fmtp:<payload_type> <fmtp>
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* m=audio 1234 RTP/AVP 98
|
||||
* a=rtpmap:98 AMR/8000
|
||||
* a=fmtp:98 octet-align=1;mode-set=0,2,4,7
|
||||
*/
|
||||
struct osmo_sdp_codec {
|
||||
/* Payload type number ("payload-type"), like 3 for GSM-FR. Limited to 0..127. */
|
||||
int8_t payload_type;
|
||||
|
||||
/* Encoding name like "GSM", "AMR", "GSM-EFR".
|
||||
*
|
||||
* RFC-8866 defines no length limit on the encoding name. This API leaves it up to the caller to provide
|
||||
* sufficient space, via the SDP_SIZES_* definitions.
|
||||
*
|
||||
* encoding-name = token
|
||||
* token = 1*(token-char)
|
||||
* token-char = ALPHA / DIGIT
|
||||
* / "!" / "#" / "$" / "%" / "&"
|
||||
* / "'" ; (single quote)
|
||||
* / "*" / "+" / "-" / "." / "^" / "_"
|
||||
* / "`" ; (Grave accent)
|
||||
*/
|
||||
char *encoding_name;
|
||||
|
||||
/* Samplerate ("clock-rate"), usually 8000 for GSM. */
|
||||
unsigned int rate;
|
||||
|
||||
/* Codec parameters as supplied in SDP line 'a=fmtp:<payload-type> <format-specific-params>'. This holds only
|
||||
* the 'format-specific-params' bytestring. For example, for SDP line 'a=fmtp:123 param1=val1;param2=val2', this
|
||||
* holds only the , "param1=val1;param2=val2" part. For the buffer size, see fmtp_size. */
|
||||
char *fmtp;
|
||||
|
||||
/* Entry used by osmo_sdp_codec_list. */
|
||||
struct llist_head entry;
|
||||
|
||||
/* For future extension, always set to false. */
|
||||
bool v2;
|
||||
};
|
||||
|
||||
struct osmo_sdp_codec *osmo_sdp_codec_alloc(void *ctx);
|
||||
|
||||
int osmo_sdp_codec_set(struct osmo_sdp_codec *c,
|
||||
int8_t payload_type, const char *encoding_name, unsigned int rate, const char *fmtp);
|
||||
int osmo_sdp_codec_set_encoding_name(struct osmo_sdp_codec *c, const char *encoding_name);
|
||||
int osmo_sdp_codec_set_fmtp(struct osmo_sdp_codec *c, const char *fmtp);
|
||||
|
||||
bool osmo_sdp_codec_is_set(const struct osmo_sdp_codec *a);
|
||||
|
||||
int osmo_sdp_codec_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_codec *codec);
|
||||
char *osmo_sdp_codec_to_str_c(void *ctx, const struct osmo_sdp_codec *codec);
|
||||
|
||||
int osmo_sdp_codec_from_str(struct osmo_sdp_codec *dst, const char *str, int str_len);
|
||||
|
||||
enum osmo_sdp_cmp {
|
||||
OSMO_SDP_CMP_IGNORE = 0,
|
||||
OSMO_SDP_CMP_EQUIVALENT,
|
||||
OSMO_SDP_CMP_EXACT,
|
||||
};
|
||||
|
||||
/*! Indicate how to match SDP codecs to various osmo_sdp_*() functions.
|
||||
* Callers may define own flags, or use predefined instances:
|
||||
* osmo_sdp_codec_cmp_exact, osmo_sdp_codec_cmp_equivalent, ...
|
||||
*
|
||||
* For example, to trigger some action if any item has changed, set all items to true / OSMO_SDP_CMP_EXACT (see
|
||||
* osmo_sdp_codec_cmp_exact).
|
||||
* To find codecs that are the same between two SDP sessions, set payload_type=false and fmtp=OSMO_SDP_CMP_EQUIVALENT
|
||||
* (see osmo_sdp_codec_cmp_equivalent).
|
||||
* To just list all contained "AMR" codecs, set only encoding_name=true (see osmo_sdp_codec_cmp_name).
|
||||
*/
|
||||
struct osmo_sdp_codec_cmp_flags {
|
||||
/*! true = compare payload type numbers 1:1; false = ignore. */
|
||||
bool payload_type;
|
||||
/*! true = compare encoding_name 1:1; false = ignore. */
|
||||
bool encoding_name;
|
||||
/*! true = compare rate 1:1; false = ignore. */
|
||||
bool rate;
|
||||
/*! OSMO_SDP_CMP_IGNORE = ignore fmtp;
|
||||
* OSMO_SDP_CMP_EQUIVALENT = use osmo_sdp_fmtp_amr_match() for AMR, otherwise compare 1:1;
|
||||
* OSMO_SDP_CMP_EXACT = compare 1:1.
|
||||
*/
|
||||
enum osmo_sdp_cmp fmtp;
|
||||
};
|
||||
|
||||
extern const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_exact;
|
||||
extern const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_equivalent;
|
||||
extern const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_name;
|
||||
|
||||
int osmo_sdp_codec_cmp(const struct osmo_sdp_codec *a, const struct osmo_sdp_codec *b,
|
||||
const struct osmo_sdp_codec_cmp_flags *cmp);
|
||||
69
include/osmocom/sdp/sdp_codec_list.h
Normal file
69
include/osmocom/sdp/sdp_codec_list.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/* Public API for codec management in SDP messages: list of struct osmo_sdp_codec. */
|
||||
/*
|
||||
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/sdp/sdp_codec.h>
|
||||
|
||||
struct osmo_sdp_codec_list {
|
||||
struct llist_head list;
|
||||
|
||||
/* For future extension, always set to false. */
|
||||
bool v2;
|
||||
};
|
||||
|
||||
struct osmo_sdp_codec_list *osmo_sdp_codec_list_alloc(void *ctx);
|
||||
void osmo_sdp_codec_list_free_items(struct osmo_sdp_codec_list *codec_list);
|
||||
|
||||
int8_t osmo_sdp_codec_list_get_unused_dyn_pt_nr(const struct osmo_sdp_codec_list *codec_list, int8_t suggest_pt_nr);
|
||||
|
||||
struct osmo_sdp_codec *osmo_sdp_codec_list_add_empty(struct osmo_sdp_codec_list *codec_list);
|
||||
struct osmo_sdp_codec *osmo_sdp_codec_list_add(struct osmo_sdp_codec_list *codec_list,
|
||||
const struct osmo_sdp_codec *codec,
|
||||
const struct osmo_sdp_codec_cmp_flags *once, bool pick_unused_pt_nr);
|
||||
|
||||
int osmo_sdp_codec_list_remove(struct osmo_sdp_codec_list *codec_list, const struct osmo_sdp_codec *codec,
|
||||
const struct osmo_sdp_codec_cmp_flags *cmpf);
|
||||
void osmo_sdp_codec_list_remove_entry(struct osmo_sdp_codec *codec);
|
||||
|
||||
int osmo_sdp_codec_list_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_codec_list *codec_list, bool summarize);
|
||||
char *osmo_sdp_codec_list_to_str_c(void *ctx, const struct osmo_sdp_codec_list *codec_list, bool summarize);
|
||||
|
||||
struct osmo_sdp_codec *osmo_sdp_codec_list_first(const struct osmo_sdp_codec_list *list);
|
||||
int osmo_sdp_codec_list_move_to_first(struct osmo_sdp_codec_list *codec_list, const struct osmo_sdp_codec *codec,
|
||||
const struct osmo_sdp_codec_cmp_flags *cmpf);
|
||||
|
||||
#define osmo_sdp_codec_list_foreach(STRUCT_SDP_CODEC_P, SDP_CODEC_LIST) \
|
||||
llist_for_each_entry(STRUCT_SDP_CODEC_P, &(SDP_CODEC_LIST)->list, entry)
|
||||
#define osmo_sdp_codec_list_foreach_safe(STRUCT_SDP_CODEC_P, SAFE_P, SDP_CODEC_LIST) \
|
||||
llist_for_each_entry_safe(STRUCT_SDP_CODEC_P, SAFE_P, &(SDP_CODEC_LIST)->list, entry)
|
||||
|
||||
int osmo_sdp_codec_list_cmp(const struct osmo_sdp_codec_list *a, const struct osmo_sdp_codec_list *b,
|
||||
const struct osmo_sdp_codec_cmp_flags *cmpf);
|
||||
|
||||
void osmo_sdp_codec_list_intersection(struct osmo_sdp_codec_list *dst, const struct osmo_sdp_codec_list *other,
|
||||
const struct osmo_sdp_codec_cmp_flags *cmpf,
|
||||
bool translate_payload_type_numbers);
|
||||
|
||||
struct osmo_sdp_codec *osmo_sdp_codec_list_by_payload_type(struct osmo_sdp_codec_list *codec_list, int8_t payload_type);
|
||||
bool osmo_sdp_codec_list_is_empty(const struct osmo_sdp_codec_list *codec_list);
|
||||
18
include/osmocom/sdp/sdp_internal.h
Normal file
18
include/osmocom/sdp/sdp_internal.h
Normal file
@@ -0,0 +1,18 @@
|
||||
/* Internal header for non-public API shared across .c files */
|
||||
#pragma once
|
||||
|
||||
struct token {
|
||||
const char *start;
|
||||
const char *end;
|
||||
};
|
||||
|
||||
/* Copy a string from [start,end[, return as talloc allocated under ctx in *dst.
|
||||
* If *dst is non-NULL, talloc_free(*dst) first. */
|
||||
void token_copy(void *ctx, char **dst, const struct token *t);
|
||||
|
||||
const char *token_chr(const struct token *src, char c);
|
||||
const char *token_chrs(const struct token *src, const char *chrs);
|
||||
void token_next(struct token *t, const char *str, const char *end, const char *separators);
|
||||
|
||||
const char *token_to_int64(int64_t *result, const struct token *t, int base, int min_val, int max_val);
|
||||
const char *token_to_int(int *result, const struct token *t, int base, int min_val, int max_val);
|
||||
105
include/osmocom/sdp/sdp_msg.h
Normal file
105
include/osmocom/sdp/sdp_msg.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/* Public API for SDP message encoding and decoding */
|
||||
/*
|
||||
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/sockaddr_str.h>
|
||||
|
||||
#include <osmocom/sdp/sdp_codec.h>
|
||||
#include <osmocom/sdp/sdp_codec_list.h>
|
||||
|
||||
/* Media Direction Attributes "a=recvonly", "a=sendrecv", "a=sendonly", "a=inactive" RFC-8866 6.7. */
|
||||
enum osmo_sdp_media_direcion_e {
|
||||
OSMO_SDP_MDIR_UNSET = 0,
|
||||
OSMO_SDP_MDIR_RECVONLY = 1,
|
||||
OSMO_SDP_MDIR_SENDRECV = 2,
|
||||
OSMO_SDP_MDIR_SENDONLY = 3,
|
||||
OSMO_SDP_MDIR_INACTIVE = 4,
|
||||
};
|
||||
|
||||
/* Session Description Protocol (SDP) message, RFC-8866. */
|
||||
struct osmo_sdp_msg {
|
||||
/* 5.2 Origin ("o="). */
|
||||
struct {
|
||||
struct osmo_sockaddr_str addr;
|
||||
char *username;
|
||||
char *sess_id;
|
||||
char *sess_version;
|
||||
} origin;
|
||||
|
||||
/* 5.3 Session Name ("s="). */
|
||||
char *session_name;
|
||||
|
||||
/* 5.7 Connection Information ("c=") and port from 5.14 Media Descriptions ("m="). */
|
||||
struct osmo_sockaddr_str rtp;
|
||||
|
||||
/* 5.9. Time Active ("t="). */
|
||||
struct {
|
||||
int64_t start;
|
||||
int64_t stop;
|
||||
} time_active;
|
||||
|
||||
/* 6.4 "a=ptime:<val>". */
|
||||
unsigned int ptime;
|
||||
|
||||
/* 6.7 "a=sendrecv"... */
|
||||
enum osmo_sdp_media_direcion_e media_direction;
|
||||
|
||||
/* List of codecs defined in the SDP message.
|
||||
* This should not be NULL -- osmo_sdp_msg_alloc() returns an empty osmo_sdp_codec_list instance, ready for
|
||||
* adding codecs.
|
||||
* Combination of:
|
||||
* - payload_type numbers from 5.14 Media Descriptions ("m="),
|
||||
* - 6.6 "a=rtpmap",
|
||||
* - 6.15 Format Parameters "a=fmtp".
|
||||
*/
|
||||
struct osmo_sdp_codec_list *codecs;
|
||||
|
||||
/* For future extension, always set to false. */
|
||||
bool v2;
|
||||
};
|
||||
|
||||
struct osmo_sdp_msg_decode_ret {
|
||||
int rc;
|
||||
/* If rc != 0 */
|
||||
struct {
|
||||
/* Point at the position that caused the error, in the src string. */
|
||||
const char *at_input_str;
|
||||
/* Nr of characters at *src_str that are relevant to the error. */
|
||||
size_t at_input_str_len;
|
||||
} error;
|
||||
/* Pointer to the remaining part of src after parsing one SDP message.
|
||||
* For example, in MGCP, there may be multiple SDP messages concatenated. */
|
||||
const char *src_remain;
|
||||
};
|
||||
|
||||
struct osmo_sdp_msg *osmo_sdp_msg_alloc(void *ctx);
|
||||
|
||||
struct osmo_sdp_msg *osmo_sdp_msg_decode(void *ctx, const char *src_str, int src_str_len,
|
||||
struct osmo_sdp_msg_decode_ret *ret);
|
||||
|
||||
int osmo_sdp_msg_encode_buf(char *dst, size_t dst_size, const struct osmo_sdp_msg *sdp);
|
||||
char *osmo_sdp_msg_encode_c(void *ctx, const struct osmo_sdp_msg *sdp);
|
||||
|
||||
int osmo_sdp_msg_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_msg *sdp, bool summarize);
|
||||
char *osmo_sdp_msg_to_str_c(void *ctx, const struct osmo_sdp_msg *sdp, bool summarize);
|
||||
39
include/osmocom/sdp/sdp_strings.h
Normal file
39
include/osmocom/sdp/sdp_strings.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/* Central definition of string tokens used for parsing and composing SDP messages */
|
||||
#pragma once
|
||||
|
||||
#define OSMO_SDP_STR_MEDIA "m"
|
||||
#define OSMO_SDP_STR_ATTRIB "a"
|
||||
#define OSMO_SDP_STR_TIME_ACTIVE "t"
|
||||
|
||||
#define OSMO_SDP_STR_RTPMAP "rtpmap"
|
||||
#define OSMO_SDP_STR_FMTP "fmtp"
|
||||
#define OSMO_SDP_STR_PTIME "ptime"
|
||||
|
||||
/*! "a=foo:" */
|
||||
#define OSMO_SDP_A_PREFIX(STR) OSMO_SDP_STR_ATTRIB "=" STR ":"
|
||||
|
||||
/*! "a=fmtp:" */
|
||||
#define OSMO_SDP_STR_A_FMTP OSMO_SDP_A_PREFIX(OSMO_SDP_STR_FMTP)
|
||||
|
||||
/* Media Direction Attributes "a=recvonly", "a=sendrecv", "a=sendonly", "a=inactive" RFC-8866 6.7. */
|
||||
#define OSMO_SDP_STR_RECVONLY "recvonly"
|
||||
#define OSMO_SDP_STR_SENDRECV "sendrecv"
|
||||
#define OSMO_SDP_STR_SENDONLY "sendonly"
|
||||
#define OSMO_SDP_STR_INACTIVE "inactive"
|
||||
|
||||
/* AMR related tokens */
|
||||
|
||||
#define OSMO_SDP_STR_AMR_OCTET_ALIGN "octet-align"
|
||||
|
||||
/*! "octet-align=1" */
|
||||
#define OSMO_SDP_STR_AMR_OCTET_ALIGN_1 OSMO_SDP_STR_AMR_OCTET_ALIGN "=1"
|
||||
|
||||
/*! "octet-align=0".
|
||||
* According to spec [1], "octet-align=0" is identical to omitting 'octet-align' entirely. In Osmocom practice, whether
|
||||
* or not "octet-align=0" is present can make a big difference for osmo-mgw versions 1.12 and older, which do not heed
|
||||
* [1].
|
||||
*
|
||||
* spec [1]: RFC4867, see details in description of osmo_sdp_fmtp_amr_is_octet_aligned().
|
||||
*/
|
||||
#define OSMO_SDP_STR_AMR_OCTET_ALIGN_0 OSMO_SDP_STR_AMR_OCTET_ALIGN "=0"
|
||||
|
||||
10
libosmo-sdp.pc.in
Normal file
10
libosmo-sdp.pc.in
Normal file
@@ -0,0 +1,10 @@
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: Osmocom Session Description Protocol coding library
|
||||
Description: C Utility Library
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -losmo-sdp
|
||||
Cflags: -I${includedir}/
|
||||
@@ -15,6 +15,7 @@ AM_CFLAGS = \
|
||||
|
||||
# Libraries
|
||||
SUBDIRS = \
|
||||
libosmo-sdp \
|
||||
libosmo-mgcp-client \
|
||||
libosmo-mgcp \
|
||||
$(NULL)
|
||||
|
||||
@@ -19,7 +19,7 @@ AM_LDFLAGS = \
|
||||
|
||||
# This is not at all related to the release version, but a range of supported
|
||||
# API versions. Read TODO_RELEASE in the source tree's root!
|
||||
MGCP_CLIENT_LIBVERSION=13:0:1
|
||||
MGCP_CLIENT_LIBVERSION=14:0:0
|
||||
|
||||
lib_LTLIBRARIES = \
|
||||
libosmo-mgcp-client.la \
|
||||
@@ -31,7 +31,6 @@ libosmo_mgcp_client_la_SOURCES = \
|
||||
mgcp_client_fsm.c \
|
||||
mgcp_client_endpoint_fsm.c \
|
||||
mgcp_client_pool.c \
|
||||
fmtp.c \
|
||||
$(NULL)
|
||||
|
||||
libosmo_mgcp_client_la_LDFLAGS = \
|
||||
|
||||
@@ -105,6 +105,94 @@ enum mgcp_codecs map_str_to_codec(const char *str)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check the ptmap for illegal mappings */
|
||||
static int check_ptmap(const struct ptmap *ptmap)
|
||||
{
|
||||
/* Check if there are mappings that leave the IANA assigned dynamic
|
||||
* payload type range. Under normal conditions such mappings should
|
||||
* not occur */
|
||||
|
||||
/* Its ok to have a 1:1 mapping in the statically defined
|
||||
* range, this won't hurt */
|
||||
if (ptmap->codec == ptmap->pt)
|
||||
return 0;
|
||||
|
||||
if (ptmap->codec < 96 || ptmap->codec > 127)
|
||||
goto error;
|
||||
if (ptmap->pt < 96 || ptmap->pt > 127)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
error:
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"ptmap contains illegal mapping: codec=%u maps to pt=%u\n",
|
||||
ptmap->codec, ptmap->pt);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*! Map a codec to a payload type.
|
||||
* \ptmap[in] payload pointer to payload type map with specified payload types.
|
||||
* \ptmap[in] ptmap_len length of the payload type map.
|
||||
* \ptmap[in] codec the codec for which the payload type should be looked up.
|
||||
* \returns assigned payload type */
|
||||
unsigned int map_codec_to_pt(const struct ptmap *ptmap, unsigned int ptmap_len,
|
||||
enum mgcp_codecs codec)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
/*! Note: If the payload type map is empty or the codec is not found
|
||||
* in the map, then a 1:1 mapping is performed. If the codec falls
|
||||
* into the statically defined range or if the mapping table isself
|
||||
* tries to map to the statically defined range, then the mapping
|
||||
* is also ignored and a 1:1 mapping is performed instead. */
|
||||
|
||||
/* we may return the codec directly since enum mgcp_codecs directly
|
||||
* corresponds to the statically assigned payload types */
|
||||
if (codec < 96 || codec > 127)
|
||||
return codec;
|
||||
|
||||
for (i = 0; i < ptmap_len; i++) {
|
||||
/* Skip illegal map entries */
|
||||
if (check_ptmap(ptmap) == 0 && ptmap->codec == codec)
|
||||
return ptmap->pt;
|
||||
ptmap++;
|
||||
}
|
||||
|
||||
/* If nothing is found, do not perform any mapping */
|
||||
return codec;
|
||||
}
|
||||
|
||||
/*! Map a payload type to a codec.
|
||||
* \ptmap[in] payload pointer to payload type map with specified payload types.
|
||||
* \ptmap[in] ptmap_len length of the payload type map.
|
||||
* \ptmap[in] payload type for which the codec should be looked up.
|
||||
* \returns codec that corresponds to the specified payload type */
|
||||
enum mgcp_codecs map_pt_to_codec(struct ptmap *ptmap, unsigned int ptmap_len,
|
||||
unsigned int pt)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
/*! Note: If the payload type map is empty or the payload type is not
|
||||
* found in the map, then a 1:1 mapping is performed. If the payload
|
||||
* type falls into the statically defined range or if the mapping
|
||||
* table isself tries to map to the statically defined range, then
|
||||
* the mapping is also ignored and a 1:1 mapping is performed
|
||||
* instead. */
|
||||
|
||||
/* See also note in map_codec_to_pt() */
|
||||
if (pt < 96 || pt > 127)
|
||||
return pt;
|
||||
|
||||
for (i = 0; i < ptmap_len; i++) {
|
||||
if (check_ptmap(ptmap) == 0 && ptmap->pt == pt)
|
||||
return ptmap->codec;
|
||||
ptmap++;
|
||||
}
|
||||
|
||||
/* If nothing is found, do not perform any mapping */
|
||||
return pt;
|
||||
}
|
||||
|
||||
static void _mgcp_client_conf_init(struct mgcp_client_conf *conf)
|
||||
{
|
||||
/* NULL and -1 default to MGCP_CLIENT_*_DEFAULT values */
|
||||
@@ -300,12 +388,11 @@ static int mgcp_parse_audio_ptime_rtpmap(struct mgcp_response *r, const char *li
|
||||
{
|
||||
unsigned int pt;
|
||||
unsigned int i;
|
||||
char codec_resp[256];
|
||||
char codec_resp[64];
|
||||
int rc;
|
||||
|
||||
#define A_PTIME "a=ptime:"
|
||||
#define A_RTPMAP "a=rtpmap:"
|
||||
#define A_FMTP "a=fmtp:"
|
||||
|
||||
if (osmo_str_startswith(line, A_PTIME)) {
|
||||
if (sscanf(line, A_PTIME "%u", &r->ptime) != 1) {
|
||||
@@ -314,7 +401,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 %255s", &pt, codec_resp) != 2) {
|
||||
if (sscanf(line, A_RTPMAP "%d %63s", &pt, codec_resp) != 2) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Failed to parse SDP parameter, invalid rtpmap: %s\n", osmo_quote_str(line, -1));
|
||||
return -EINVAL;
|
||||
@@ -353,42 +440,6 @@ 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;
|
||||
@@ -677,7 +728,7 @@ static struct mgcp_response_pending *mgcp_client_response_pending_get(
|
||||
/* Feed an MGCP message into the receive processing.
|
||||
* Parse the head and call any callback registered for the transaction id found
|
||||
* in the MGCP message. This is normally called directly from the internal
|
||||
* mgcp_do_read that reads from the socket connected to the MGCP gateway. This
|
||||
* mgcp_read_cb that reads from the socket connected to the MGCP gateway. This
|
||||
* function is published mainly to be able to feed data from the test suite.
|
||||
*/
|
||||
int mgcp_client_rx(struct mgcp_client *mgcp, struct msgb *msg)
|
||||
@@ -730,55 +781,58 @@ error:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int mgcp_do_read(struct osmo_fd *fd)
|
||||
static void mgcp_read_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg)
|
||||
{
|
||||
struct mgcp_client *mgcp = fd->data;
|
||||
struct msgb *msg;
|
||||
int ret;
|
||||
struct mgcp_client *mgcp = osmo_iofd_get_data(iofd);
|
||||
|
||||
msg = msgb_alloc_headroom(4096, 128, "mgcp_from_gw");
|
||||
if (!msg) {
|
||||
LOGPMGW(mgcp, LOGL_ERROR, "Failed to allocate MGCP message.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* msgb_tailroom() is basically (4096 - 128); -1 is for '\0' */
|
||||
ret = read(fd->fd, msg->data, msgb_tailroom(msg) - 1);
|
||||
if (ret <= 0) {
|
||||
if (res <= 0) {
|
||||
char errbuf[128] = "";
|
||||
strerror_r(-res, errbuf, sizeof(errbuf));
|
||||
LOGPMGW(mgcp, LOGL_ERROR, "Failed to read: %s: %d='%s'\n",
|
||||
osmo_sock_get_name2(fd->fd), errno, strerror(errno));
|
||||
osmo_iofd_get_name(iofd), res, errbuf);
|
||||
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
return;
|
||||
}
|
||||
|
||||
msg->l2h = msgb_put(msg, ret);
|
||||
ret = mgcp_client_rx(mgcp, msg);
|
||||
msg->l2h = msg->head;
|
||||
mgcp_client_rx(mgcp, msg);
|
||||
talloc_free(msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mgcp_do_write(struct osmo_fd *fd, struct msgb *msg)
|
||||
static int mgcp_do_write(struct mgcp_client *mgcp, struct msgb *msg)
|
||||
{
|
||||
int ret;
|
||||
struct mgcp_client *mgcp = fd->data;
|
||||
|
||||
LOGPMGW(mgcp, LOGL_DEBUG, "Tx MGCP: %s: len=%u '%s'...\n",
|
||||
osmo_sock_get_name2(fd->fd), msg->len,
|
||||
osmo_iofd_get_name(mgcp->iofd), msg->len,
|
||||
osmo_escape_str((const char *)msg->data, OSMO_MIN(42, msg->len)));
|
||||
|
||||
ret = write(fd->fd, msg->data, msg->len);
|
||||
if (OSMO_UNLIKELY(ret != msg->len))
|
||||
LOGPMGW(mgcp, LOGL_ERROR, "Failed to Tx MGCP: %s: %d='%s'; msg: len=%u '%s'...\n",
|
||||
osmo_sock_get_name2(fd->fd), errno, strerror(errno),
|
||||
msg->len, osmo_escape_str((const char *)msg->data, OSMO_MIN(42, msg->len)));
|
||||
ret = osmo_iofd_write_msgb(mgcp->iofd, msg);
|
||||
if (ret < 0)
|
||||
msgb_free(msg);
|
||||
|
||||
/* Re-arm the keepalive Tx timer: */
|
||||
if (mgcp->actual.keepalive.req_interval_sec > 0)
|
||||
osmo_timer_schedule(&mgcp->keepalive_tx_timer, mgcp->actual.keepalive.req_interval_sec, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mgcp_write_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg)
|
||||
{
|
||||
struct mgcp_client *mgcp = osmo_iofd_get_data(iofd);
|
||||
|
||||
if (OSMO_UNLIKELY(res != msg->len)) {
|
||||
char errbuf[128] = "";
|
||||
strerror_r(-res, errbuf, sizeof(errbuf));
|
||||
LOGPMGW(mgcp, LOGL_ERROR, "Failed to Tx MGCP: %s: %d='%s'; msg: len=%u '%s'...\n",
|
||||
osmo_iofd_get_name(mgcp->iofd), res, errbuf,
|
||||
msg->len, osmo_escape_str((const char *)msg->data, OSMO_MIN(42, msg->len)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const char *_mgcp_client_name_append_domain(const struct mgcp_client *mgcp, const char *name)
|
||||
{
|
||||
static char endpoint[MGCP_ENDPOINT_MAXLEN];
|
||||
@@ -810,6 +864,10 @@ static void _mgcp_client_send_dlcx(struct mgcp_client *mgcp, const char *epname)
|
||||
};
|
||||
osmo_strlcpy(mgcp_msg_dlcx.endpoint, epname, sizeof(mgcp_msg_dlcx.endpoint));
|
||||
msgb_dlcx = mgcp_msg_gen(mgcp, &mgcp_msg_dlcx);
|
||||
if (!msgb_dlcx) {
|
||||
LOGPMGW(mgcp, LOGL_ERROR, "Failed generating MGCP DLCX %s\n", epname);
|
||||
return;
|
||||
}
|
||||
mgcp_client_tx(mgcp, msgb_dlcx, &_ignore_mgcp_response, NULL);
|
||||
}
|
||||
|
||||
@@ -823,6 +881,10 @@ static void _mgcp_client_send_auep(struct mgcp_client *mgcp, const char *epname)
|
||||
};
|
||||
OSMO_STRLCPY_ARRAY(mgcp_msg_auep.endpoint, epname);
|
||||
msgb_auep = mgcp_msg_gen(mgcp, &mgcp_msg_auep);
|
||||
if (!msgb_auep) {
|
||||
LOGPMGW(mgcp, LOGL_ERROR, "Failed generating MGCP AUEP %s\n", epname);
|
||||
return;
|
||||
}
|
||||
mgcp_client_tx(mgcp, msgb_auep, &_ignore_mgcp_response, NULL);
|
||||
}
|
||||
|
||||
@@ -890,11 +952,6 @@ struct mgcp_client *mgcp_client_init(void *ctx,
|
||||
if (conf->description)
|
||||
mgcp->actual.description = talloc_strdup(mgcp, conf->description);
|
||||
|
||||
osmo_wqueue_init(&mgcp->wq, 1024);
|
||||
mgcp->wq.read_cb = mgcp_do_read;
|
||||
mgcp->wq.write_cb = mgcp_do_write;
|
||||
osmo_fd_setup(&mgcp->wq.bfd, -1, OSMO_FD_READ, osmo_wqueue_bfd_cb, mgcp, 0);
|
||||
|
||||
memcpy(&mgcp->actual.keepalive, &conf->keepalive, sizeof(conf->keepalive));
|
||||
osmo_timer_setup(&mgcp->keepalive_tx_timer, mgcp_client_keepalive_tx_timer_cb, mgcp);
|
||||
osmo_timer_setup(&mgcp->keepalive_rx_timer, mgcp_client_keepalive_rx_timer_cb, mgcp);
|
||||
@@ -902,6 +959,11 @@ struct mgcp_client *mgcp_client_init(void *ctx,
|
||||
return mgcp;
|
||||
}
|
||||
|
||||
static const struct osmo_io_ops mgcp_clnt_ioops = {
|
||||
.read_cb = mgcp_read_cb,
|
||||
.write_cb = mgcp_write_cb,
|
||||
};
|
||||
|
||||
/*! Initialize client connection (opens socket)
|
||||
* \param[in,out] mgcp MGCP client descriptor.
|
||||
* \returns 0 on success, -EINVAL on error. */
|
||||
@@ -917,19 +979,28 @@ int mgcp_client_connect(struct mgcp_client *mgcp)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = osmo_sock_init2_ofd(&mgcp->wq.bfd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, mgcp->actual.local_addr,
|
||||
mgcp->actual.local_port, mgcp->actual.remote_addr, mgcp->actual.remote_port,
|
||||
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
|
||||
rc = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, mgcp->actual.local_addr,
|
||||
mgcp->actual.local_port, mgcp->actual.remote_addr, mgcp->actual.remote_port,
|
||||
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
|
||||
if (rc < 0) {
|
||||
LOGPMGW(mgcp, LOGL_FATAL,
|
||||
"Failed to initialize socket %s:%u -> %s:%u for MGW: %s\n",
|
||||
mgcp->actual.local_addr ? mgcp->actual.local_addr : "(any)", mgcp->actual.local_port,
|
||||
mgcp->actual.remote_addr ? mgcp->actual.local_addr : "(any)", mgcp->actual.remote_port,
|
||||
strerror(errno));
|
||||
goto error_close_fd;
|
||||
goto error_free;
|
||||
}
|
||||
|
||||
LOGPMGW(mgcp, LOGL_INFO, "MGW connection: %s\n", osmo_sock_get_name2(mgcp->wq.bfd.fd));
|
||||
mgcp->iofd = osmo_iofd_setup(mgcp, rc, osmo_sock_get_name2(rc), OSMO_IO_FD_MODE_READ_WRITE,
|
||||
&mgcp_clnt_ioops, mgcp);
|
||||
if (!mgcp->iofd)
|
||||
goto error_close_fd;
|
||||
|
||||
LOGPMGW(mgcp, LOGL_INFO, "MGW connection: %s\n", osmo_iofd_get_name(mgcp->iofd));
|
||||
|
||||
osmo_iofd_register(mgcp->iofd, -1);
|
||||
osmo_iofd_set_alloc_info(mgcp->iofd, 4096, 128);
|
||||
osmo_iofd_set_txqueue_max_length(mgcp->iofd, 1024);
|
||||
|
||||
/* If configured, send a DLCX message to the endpoints that are configured to
|
||||
* be reset on startup. Usually this is a wildcarded endpoint. */
|
||||
@@ -955,9 +1026,10 @@ int mgcp_client_connect(struct mgcp_client *mgcp)
|
||||
osmo_timer_schedule(&mgcp->keepalive_rx_timer, mgcp->actual.keepalive.timeout_sec, 0);
|
||||
|
||||
return 0;
|
||||
|
||||
error_close_fd:
|
||||
close(mgcp->wq.bfd.fd);
|
||||
mgcp->wq.bfd.fd = -1;
|
||||
close(rc);
|
||||
error_free:
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -974,8 +1046,6 @@ int mgcp_client_connect2(struct mgcp_client *mgcp, unsigned int retry_n_ports)
|
||||
* \returns 0 on success, -EINVAL on error. */
|
||||
void mgcp_client_disconnect(struct mgcp_client *mgcp)
|
||||
{
|
||||
struct osmo_wqueue *wq;
|
||||
|
||||
if (!mgcp) {
|
||||
LOGP(DLMGCP, LOGL_FATAL, "MGCP client not initialized properly\n");
|
||||
return;
|
||||
@@ -986,13 +1056,10 @@ void mgcp_client_disconnect(struct mgcp_client *mgcp)
|
||||
osmo_timer_del(&mgcp->keepalive_tx_timer);
|
||||
mgcp->conn_up = false;
|
||||
|
||||
wq = &mgcp->wq;
|
||||
osmo_wqueue_clear(wq);
|
||||
LOGPMGW(mgcp, LOGL_INFO, "MGCP association: %s -- closed!\n", osmo_sock_get_name2(wq->bfd.fd));
|
||||
if (osmo_fd_is_registered(&wq->bfd))
|
||||
osmo_fd_unregister(&wq->bfd);
|
||||
close(wq->bfd.fd);
|
||||
wq->bfd.fd = -1;
|
||||
osmo_iofd_txqueue_clear(mgcp->iofd);
|
||||
LOGPMGW(mgcp, LOGL_INFO, "MGCP association: %s -- closed!\n", osmo_iofd_get_name(mgcp->iofd));
|
||||
osmo_iofd_free(mgcp->iofd);
|
||||
mgcp->iofd = NULL;
|
||||
}
|
||||
|
||||
/*! Get the IP-Aaddress of the associated MGW as string.
|
||||
@@ -1146,10 +1213,9 @@ int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
|
||||
goto mgcp_tx_error;
|
||||
}
|
||||
|
||||
rc = osmo_wqueue_enqueue(&mgcp->wq, msg);
|
||||
rc = mgcp_do_write(mgcp, msg);
|
||||
if (rc) {
|
||||
LOGPMGW(mgcp, LOGL_FATAL, "Could not queue message to MGW\n");
|
||||
msgb_free(msg);
|
||||
goto mgcp_tx_error;
|
||||
} else
|
||||
LOGPMGW(mgcp, LOGL_DEBUG, "Queued %u bytes for MGW\n",
|
||||
@@ -1254,6 +1320,36 @@ static int add_lco(struct msgb *msg, struct mgcp_msg *mgcp_msg)
|
||||
#undef MSGB_PRINTF_OR_RET
|
||||
}
|
||||
|
||||
/* Helper function to obtain local IP address used in MGCP towards MGW,
|
||||
* in string format, to fill the SDP "Origin" ("o=") field.
|
||||
* return 0 on success, negative on error.
|
||||
*/
|
||||
static int get_mgcp_local_addr(const struct mgcp_client *mgcp, char *local_ip, size_t local_ip_len)
|
||||
{
|
||||
int fd;
|
||||
|
||||
/* Try to get the socket local IP address if available: */
|
||||
if (mgcp->iofd && ((fd = osmo_iofd_get_fd(mgcp->iofd)) >= 0)) {
|
||||
if (osmo_sock_get_local_ip(fd, local_ip, local_ip_len) == 0)
|
||||
return 0;
|
||||
/* else: continue below */
|
||||
}
|
||||
|
||||
/* If MGCP local address was explicitly specified in config, use it: */
|
||||
if (mgcp->actual.local_addr) {
|
||||
osmo_strlcpy(local_ip, mgcp->actual.local_addr, local_ip_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Guess our local address based on system routing towards MGW: */
|
||||
OSMO_ASSERT(local_ip_len >= INET6_ADDRSTRLEN);
|
||||
if (osmo_sock_local_ip(local_ip, mgcp->actual.remote_addr) == 0)
|
||||
return 0;
|
||||
|
||||
LOGPMGW(mgcp, LOGL_ERROR, "Could not determine local IP-Address!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Helper function for mgcp_msg_gen(): Add SDP information to MGCP message */
|
||||
static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_client *mgcp)
|
||||
{
|
||||
@@ -1262,6 +1358,8 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie
|
||||
int local_ip_family, audio_ip_family;
|
||||
const char *codec;
|
||||
unsigned int pt;
|
||||
uint16_t audio_port;
|
||||
int rc;
|
||||
|
||||
#define MSGB_PRINTF_OR_RET(FMT, ARGS...) do { \
|
||||
if (msgb_printf(msg, FMT, ##ARGS) != 0) { \
|
||||
@@ -1277,11 +1375,9 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie
|
||||
MSGB_PRINTF_OR_RET("v=0\r\n");
|
||||
|
||||
/* Determine local IP-Address */
|
||||
if (osmo_sock_local_ip(local_ip, mgcp->actual.remote_addr) < 0) {
|
||||
LOGPMGW(mgcp, LOGL_ERROR,
|
||||
"Could not determine local IP-Address!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
rc = get_mgcp_local_addr(mgcp, local_ip, sizeof(local_ip));
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
local_ip_family = osmo_ip_str_type(local_ip);
|
||||
if (local_ip_family == AF_UNSPEC)
|
||||
return -EINVAL;
|
||||
@@ -1313,33 +1409,28 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie
|
||||
MSGB_PRINTF_OR_RET("t=0 0\r\n");
|
||||
|
||||
/* Add RTP address port and codecs */
|
||||
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_PORT) {
|
||||
if (mgcp_msg->audio_port == 0) {
|
||||
audio_port = 0;
|
||||
if ((mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_PORT)) {
|
||||
audio_port = mgcp_msg->audio_port;
|
||||
if (!audio_port) {
|
||||
LOGPMGW(mgcp, LOGL_ERROR,
|
||||
"Invalid port number, can not generate MGCP message\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
MSGB_PRINTF_OR_RET("m=audio %u RTP/AVP", mgcp_msg->audio_port);
|
||||
for (i = 0; i < mgcp_msg->ptmap_len; i++)
|
||||
MSGB_PRINTF_OR_RET(" %u", mgcp_msg->ptmap[i].pt);
|
||||
MSGB_PRINTF_OR_RET("\r\n");
|
||||
}
|
||||
MSGB_PRINTF_OR_RET("m=audio %u RTP/AVP", audio_port);
|
||||
for (i = 0; i < mgcp_msg->ptmap_len; i++)
|
||||
MSGB_PRINTF_OR_RET(" %u", mgcp_msg->ptmap[i].pt);
|
||||
MSGB_PRINTF_OR_RET("\r\n");
|
||||
|
||||
/* Add optional codec parameters (fmtp) */
|
||||
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)) {
|
||||
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;
|
||||
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)
|
||||
@@ -1577,8 +1668,5 @@ int ptmap_cmp(const struct ptmap *a, const struct ptmap *b)
|
||||
rc = OSMO_CMP(a->codec, b->codec);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = OSMO_CMP(a->pt, b->pt);
|
||||
if (rc)
|
||||
return rc;
|
||||
return strcmp(a->fmtp, b->fmtp);
|
||||
return OSMO_CMP(a->pt, b->pt);
|
||||
}
|
||||
|
||||
@@ -157,6 +157,7 @@ static void set_conn_mode(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *peer
|
||||
mgcp_msg->conn_mode = conn_mode;
|
||||
}
|
||||
|
||||
/* returns message buffer containing MGXP MDCX on success, NULL on error. */
|
||||
static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx)
|
||||
{
|
||||
struct mgcp_msg mgcp_msg;
|
||||
@@ -192,6 +193,7 @@ static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx)
|
||||
return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
|
||||
}
|
||||
|
||||
/* returns message buffer containing MGXP DLCX on success, NULL on error. */
|
||||
struct msgb *make_dlcx_msg(struct mgcp_ctx *mgcp_ctx)
|
||||
{
|
||||
struct mgcp_msg mgcp_msg;
|
||||
@@ -231,8 +233,10 @@ static void fsm_crcx_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
set_conn_mode(&mgcp_msg, &mgcp_ctx->conn_peer_local);
|
||||
|
||||
msg = mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
|
||||
OSMO_ASSERT(msg);
|
||||
|
||||
if (!msg) {
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
|
||||
return;
|
||||
}
|
||||
mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
|
||||
mgcp_ctx->mgw_trans_pending = true;
|
||||
rc = mgcp_client_tx(mgcp, msg, mgw_crcx_resp_cb, fi);
|
||||
@@ -624,17 +628,6 @@ static struct osmo_fsm fsm_mgcp_client = {
|
||||
.log_subsys = DLMGCP,
|
||||
};
|
||||
|
||||
/*! allocate struct to hold the description of an MGCP connection peer.
|
||||
* \param[in] ctx talloc context.
|
||||
* \returns newly-allocated and initialized struct mgcp_conn_peer. */
|
||||
struct mgcp_conn_peer *mgcp_conn_peer_alloc(void *ctx)
|
||||
{
|
||||
struct mgcp_conn_peer *peer;
|
||||
peer = talloc_zero(ctx, struct mgcp_conn_peer);
|
||||
OSMO_ASSERT(peer);
|
||||
return peer;
|
||||
}
|
||||
|
||||
/* Provide backwards compat for deprecated conn_peer->codecs[]: when the caller passes in an mgcp_conn_peer instance
|
||||
* that has codecs[] set, apply it to ptmap[] instead. */
|
||||
static void mgcp_conn_peer_compat(struct mgcp_conn_peer *conn_peer)
|
||||
|
||||
@@ -322,7 +322,7 @@ DEFUN(cfg_mgw_mgw_keepalive_req_interval,
|
||||
|
||||
/* If client already exists, apply the change immediately if possible: */
|
||||
mgcp->actual.keepalive.req_interval_sec = atoi(argv[0]);
|
||||
if (mgcp->wq.bfd.fd != -1) { /* UDP MGCP socket connected */
|
||||
if (mgcp->iofd) { /* UDP MGCP socket connected */
|
||||
if (mgcp->actual.keepalive.req_interval_sec > 0) {
|
||||
/* Re-schedule: */
|
||||
osmo_timer_schedule(&mgcp->keepalive_tx_timer, mgcp->actual.keepalive.req_interval_sec, 0);
|
||||
@@ -375,7 +375,7 @@ DEFUN(cfg_mgw_mgw_keepalive_timeout,
|
||||
|
||||
/* If client already exists, apply the change immediately if possible: */
|
||||
mgcp->actual.keepalive.timeout_sec = atoi(argv[0]);
|
||||
if (mgcp->wq.bfd.fd != -1) { /* UDP MGCP socket connected */
|
||||
if (mgcp->iofd) { /* UDP MGCP socket connected */
|
||||
if (mgcp->actual.keepalive.timeout_sec > 0) {
|
||||
/* Re-schedule: */
|
||||
osmo_timer_schedule(&mgcp->keepalive_rx_timer, mgcp->actual.keepalive.timeout_sec, 0);
|
||||
@@ -680,7 +680,7 @@ DEFUN(mgw_show, mgw_show_cmd, "show mgw-pool", SHOW_STR "Display information abo
|
||||
const struct mgcp_client *cli = pool_member->client;
|
||||
vty_out(vty, "%% MGW %s%s", mgcp_client_pool_member_name(pool_member), VTY_NEWLINE);
|
||||
vty_out(vty, "%% MGCP link: %s,%s%s",
|
||||
cli && cli->wq.bfd.fd != -1 ? "connected" : "disconnected",
|
||||
cli && cli->iofd ? "connected" : "disconnected",
|
||||
cli && cli->conn_up ?
|
||||
((cli->actual.keepalive.timeout_sec > 0) ? "UP" : "MAYBE") :
|
||||
"DOWN",
|
||||
|
||||
@@ -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 \
|
||||
@@ -42,5 +42,13 @@ libosmo_mgcp_a_SOURCES = \
|
||||
mgcp_ratectr.c \
|
||||
mgcp_e1.c \
|
||||
mgcp_iuup.c \
|
||||
$(top_srcdir)/src/libosmo-mgcp-client/fmtp.c \
|
||||
$(NULL)
|
||||
|
||||
libosmo_mgcp_la_LIBADD = \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBOSMONETIF_LIBS) \
|
||||
$(LIBOSMOABIS_LIBS) \
|
||||
$(LIBOSMOTRAU_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_trunk.h>
|
||||
#include <osmocom/mgcp/mgcp_codec.h>
|
||||
#include <osmocom/mgcp_client/fmtp.h>
|
||||
#include <errno.h>
|
||||
|
||||
/* Helper function to dump codec information of a specified codec to a printable
|
||||
@@ -117,9 +116,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] fmtp optional codec parameters (set to NULL when unused).
|
||||
* \param[in] param optional codec parameters (set to NULL when unused).
|
||||
* \returns 0 on success, -EINVAL on failure. */
|
||||
int mgcp_codec_add2(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const char *fmtp)
|
||||
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param)
|
||||
{
|
||||
int rate;
|
||||
int channels;
|
||||
@@ -262,16 +261,12 @@ int mgcp_codec_add2(struct mgcp_conn_rtp *conn, int payload_type, const char *au
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
/* Copy over optional codec parameters */
|
||||
if (param) {
|
||||
codec->param = *param;
|
||||
codec->param_present = true;
|
||||
} else
|
||||
codec->param_present = false;
|
||||
|
||||
conn->end.codecs_assigned++;
|
||||
return 0;
|
||||
@@ -281,33 +276,23 @@ error:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*! 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 = OSMO_SDP_AMR_SET_OCTET_ALIGN(param->amr_octet_aligned);
|
||||
|
||||
return mgcp_codec_add2(conn, payload_type, audio_name, fmtp);
|
||||
}
|
||||
|
||||
/* 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
|
||||
*/
|
||||
bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec)
|
||||
{
|
||||
/* 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);
|
||||
if (!codec->param_present)
|
||||
return false;
|
||||
if (!codec->param.amr_octet_aligned_present)
|
||||
return false;
|
||||
return codec->param.amr_octet_aligned;
|
||||
}
|
||||
|
||||
/* Compare two codecs, all parameters must match up */
|
||||
@@ -429,26 +414,6 @@ static struct mgcp_rtp_codec *codec_find_convertible(struct mgcp_conn_rtp *conn,
|
||||
int mgcp_codec_decide(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst)
|
||||
{
|
||||
unsigned int i;
|
||||
if (log_check_level(DLMGCP, LOGL_DEBUG)) {
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "%s(): src.codecs_assigned=%d dst.codecs_assigned=%d\n",
|
||||
__func__,
|
||||
conn_src ? conn_src->end.codecs_assigned : 0,
|
||||
conn_dst ? conn_dst->end.codecs_assigned : 0);
|
||||
if (conn_src) {
|
||||
for (i = 0; i < conn_src->end.codecs_assigned; i++) {
|
||||
struct mgcp_rtp_codec *c = &conn_src->end.codecs[i];
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "src.codecs[%d]: %d %s %s %s\n",
|
||||
i, c->payload_type, c->audio_name, c->subtype_name, c->fmtp);
|
||||
}
|
||||
}
|
||||
if (conn_dst) {
|
||||
for (i = 0; i < conn_dst->end.codecs_assigned; i++) {
|
||||
struct mgcp_rtp_codec *c = &conn_dst->end.codecs[i];
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "dst.codecs[%d]: %d %s %s %s\n",
|
||||
i, c->payload_type, c->audio_name, c->subtype_name, c->fmtp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* In case no destination connection is available (yet), or in case the destination connection exists but has
|
||||
* no codecs assigned, we are forced to make a simple tentative decision:
|
||||
@@ -490,7 +455,6 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn
|
||||
}
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR, "no matching codec found\n");
|
||||
if (conn_dst->end.codecs_assigned)
|
||||
conn_dst->end.codec = &conn_dst->end.codecs[0];
|
||||
else
|
||||
@@ -507,13 +471,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->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_NAME_AMR_OCTET_ALIGN);
|
||||
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;
|
||||
}
|
||||
|
||||
/* Find the payload type number configured for a specific codec by SDP.
|
||||
|
||||
@@ -74,7 +74,7 @@ static int mgcp_alloc_id(struct mgcp_endpoint *endp, char *id)
|
||||
|
||||
/* ensure that the generated conn_id is unique
|
||||
* for this endpoint */
|
||||
if (!mgcp_conn_get_rtp(endp, id_hex)) {
|
||||
if (!mgcp_endp_get_conn_rtp(endp, id_hex)) {
|
||||
osmo_strlcpy(id, id_hex, MGCP_CONN_ID_MAXLEN);
|
||||
return 0;
|
||||
}
|
||||
@@ -106,8 +106,8 @@ static int mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *
|
||||
/* backpointer to the generic part of the connection */
|
||||
conn->u.rtp.conn = conn;
|
||||
|
||||
end->rtp.fd = -1;
|
||||
end->rtcp.fd = -1;
|
||||
end->rtp = NULL;
|
||||
end->rtcp = NULL;
|
||||
memset(&end->addr, 0, sizeof(end->addr));
|
||||
end->rtcp_port = 0;
|
||||
|
||||
@@ -137,7 +137,7 @@ static void mgcp_rtp_conn_cleanup(struct mgcp_conn_rtp *conn_rtp)
|
||||
conn_osmux_disable(conn_rtp);
|
||||
if (mgcp_conn_rtp_is_iuup(conn_rtp))
|
||||
mgcp_conn_iuup_cleanup(conn_rtp);
|
||||
mgcp_free_rtp_port(&conn_rtp->end);
|
||||
mgcp_rtp_end_free_port(&conn_rtp->end);
|
||||
rate_ctr_group_free(conn_rtp->ctrg);
|
||||
mgcp_codec_reset_all(conn_rtp);
|
||||
}
|
||||
@@ -146,7 +146,7 @@ void mgcp_conn_watchdog_cb(void *data)
|
||||
{
|
||||
struct mgcp_conn *conn = data;
|
||||
LOGPCONN(conn, DLMGCP, LOGL_ERROR, "connection timed out!\n");
|
||||
mgcp_conn_free(conn->endp, conn->id);
|
||||
mgcp_conn_free(conn);
|
||||
}
|
||||
|
||||
void mgcp_conn_watchdog_kick(struct mgcp_conn *conn)
|
||||
@@ -212,58 +212,6 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
|
||||
return conn;
|
||||
}
|
||||
|
||||
/*! find a connection by its ID.
|
||||
* \param[in] endp associated endpoint
|
||||
* \param[in] id identification number of the connection
|
||||
* \returns pointer to allocated connection, NULL if not found */
|
||||
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id)
|
||||
{
|
||||
struct mgcp_conn *conn;
|
||||
const char *id_upper;
|
||||
const char *conn_id;
|
||||
|
||||
if (!id || !*id)
|
||||
return NULL;
|
||||
|
||||
/* Ignore leading zeros in needle */
|
||||
while (*id == '0')
|
||||
id++;
|
||||
|
||||
/* Use uppercase to compare identifiers, to avoid mismatches: RFC3435 2.1.3.2 "Names of
|
||||
* Connections" defines the id as a hex string, so clients may return lower case hex even though
|
||||
* we sent upper case hex in the CRCX response. */
|
||||
id_upper = osmo_str_toupper(id);
|
||||
|
||||
llist_for_each_entry(conn, &endp->conns, entry) {
|
||||
/* Ignore leading zeros in haystack */
|
||||
for (conn_id=conn->id; *conn_id == '0'; conn_id++);
|
||||
|
||||
if (strcmp(conn_id, id_upper) == 0)
|
||||
return conn;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! find an RTP connection by its ID.
|
||||
* \param[in] endp associated endpoint
|
||||
* \param[in] id identification number of the connection
|
||||
* \returns pointer to allocated connection, NULL if not found */
|
||||
struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp,
|
||||
const char *id)
|
||||
{
|
||||
struct mgcp_conn *conn;
|
||||
|
||||
conn = mgcp_conn_get(endp, id);
|
||||
if (!conn)
|
||||
return NULL;
|
||||
|
||||
if (conn->type == MGCP_CONN_TYPE_RTP)
|
||||
return &conn->u.rtp;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void aggregate_rtp_conn_stats(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn_rtp)
|
||||
{
|
||||
struct rate_ctr_group *all_stats = endp->trunk->ratectr.all_rtp_conn_stats;
|
||||
@@ -286,21 +234,21 @@ static void aggregate_rtp_conn_stats(struct mgcp_endpoint *endp, struct mgcp_con
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(all_stats, RTP_NUM_CONNECTIONS));
|
||||
}
|
||||
|
||||
/*! free a connection by its ID.
|
||||
* \param[in] endp associated endpoint
|
||||
* \param[in] id identification number of the connection */
|
||||
void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id)
|
||||
/*! free a connection
|
||||
* \param[in] conn the conn to free. May be NULL.
|
||||
*/
|
||||
void mgcp_conn_free(struct mgcp_conn *conn)
|
||||
{
|
||||
struct mgcp_conn *conn;
|
||||
struct mgcp_conn_rtp *conn_rtp;
|
||||
|
||||
conn = mgcp_conn_get(endp, id);
|
||||
if (!conn)
|
||||
return;
|
||||
|
||||
switch (conn->type) {
|
||||
case MGCP_CONN_TYPE_RTP:
|
||||
aggregate_rtp_conn_stats(endp, &conn->u.rtp);
|
||||
mgcp_rtp_conn_cleanup(&conn->u.rtp);
|
||||
conn_rtp = mgcp_conn_get_conn_rtp(conn);
|
||||
aggregate_rtp_conn_stats(conn->endp, conn_rtp);
|
||||
mgcp_rtp_conn_cleanup(conn_rtp);
|
||||
break;
|
||||
default:
|
||||
/* NOTE: This should never be called with an
|
||||
@@ -310,43 +258,40 @@ void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id)
|
||||
}
|
||||
|
||||
osmo_timer_del(&conn->watchdog);
|
||||
mgcp_endp_remove_conn(endp, conn);
|
||||
mgcp_endp_remove_conn(conn->endp, conn);
|
||||
/* WARN: endp may have be freed after call to mgcp_endp_remove_conn */
|
||||
talloc_free(conn);
|
||||
}
|
||||
|
||||
/*! free oldest connection in the list.
|
||||
* \param[in] endp associated endpoint */
|
||||
void mgcp_conn_free_oldest(struct mgcp_endpoint *endp)
|
||||
/*! Parse connection mode.
|
||||
* \param[in] conn Connection whose mode is being set
|
||||
* \param[in] mode Mode to set
|
||||
* \returns 0 on success, -1 on error */
|
||||
int mgcp_conn_set_mode(struct mgcp_conn *conn, enum mgcp_connection_mode mode)
|
||||
{
|
||||
struct mgcp_conn *conn;
|
||||
OSMO_ASSERT(conn);
|
||||
if (mode == MGCP_CONN_NONE) {
|
||||
LOGPCONN(conn, DLMGCP, LOGL_ERROR,
|
||||
"missing connection mode\n");
|
||||
return -1;
|
||||
}
|
||||
conn->mode = mode;
|
||||
LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "connection mode '%s' %d\n",
|
||||
mgcp_cmode_name(mode), conn->mode);
|
||||
|
||||
if (llist_empty(&endp->conns))
|
||||
return;
|
||||
/* Special handling for RTP connections */
|
||||
if (conn->type == MGCP_CONN_TYPE_RTP) {
|
||||
struct mgcp_conn_rtp *conn_rtp = mgcp_conn_get_conn_rtp(conn);
|
||||
conn_rtp->end.output_enabled = !!(conn->mode & MGCP_CONN_SEND_ONLY);
|
||||
LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "output_enabled %u\n",
|
||||
conn_rtp->end.output_enabled);
|
||||
}
|
||||
|
||||
conn = llist_last_entry(&endp->conns, struct mgcp_conn, entry);
|
||||
if (!conn)
|
||||
return;
|
||||
/* The VTY might change the connection mode at any time, so we have
|
||||
* to hold a copy of the original connection mode */
|
||||
conn->mode_orig = conn->mode;
|
||||
|
||||
mgcp_conn_free(endp, conn->id);
|
||||
}
|
||||
|
||||
/*! free all connections at once.
|
||||
* \param[in] endp associated endpoint */
|
||||
#if defined(__has_attribute)
|
||||
#if __has_attribute(no_sanitize)
|
||||
__attribute__((no_sanitize("undefined"))) /* ubsan detects a misaligned load */
|
||||
#endif
|
||||
#endif
|
||||
void mgcp_conn_free_all(struct mgcp_endpoint *endp)
|
||||
{
|
||||
struct mgcp_conn *conn;
|
||||
|
||||
/* Drop all items in the list, might be consecutive! */
|
||||
while ((conn = llist_first_entry_or_null(&endp->conns, struct mgcp_conn, entry)))
|
||||
mgcp_conn_free(endp, conn->id);
|
||||
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! dump basic connection information to human readable string.
|
||||
@@ -357,35 +302,31 @@ char *mgcp_conn_dump(struct mgcp_conn *conn)
|
||||
static char str[sizeof(conn->name)+sizeof(conn->id)+256];
|
||||
char ipbuf[INET6_ADDRSTRLEN];
|
||||
struct osmo_strbuf sb = { .buf = str, .len = sizeof(str) };
|
||||
int i;
|
||||
struct mgcp_conn_rtp *conn_rtp;
|
||||
|
||||
if (!conn)
|
||||
return "NULL";
|
||||
|
||||
switch (conn->type) {
|
||||
case MGCP_CONN_TYPE_RTP:
|
||||
conn_rtp = mgcp_conn_get_conn_rtp(conn);
|
||||
OSMO_STRBUF_PRINTF(sb, "(%s/%s C:%s r=%s:%u<->l=%s:%u",
|
||||
conn->name,
|
||||
mgcp_conn_rtp_type_name(conn->type),
|
||||
conn->id,
|
||||
osmo_sockaddr_ntop(&conn->u.rtp.end.addr.u.sa, ipbuf) ? : "NULL",
|
||||
osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa),
|
||||
conn->u.rtp.end.local_addr ? : "NULL",
|
||||
conn->u.rtp.end.local_port);
|
||||
osmo_sockaddr_ntop(&conn_rtp->end.addr.u.sa, ipbuf) ? : "NULL",
|
||||
osmo_sockaddr_port(&conn_rtp->end.addr.u.sa),
|
||||
conn_rtp->end.local_addr ? : "NULL",
|
||||
conn_rtp->end.local_port);
|
||||
|
||||
switch (conn->u.rtp.type) {
|
||||
switch (conn_rtp->type) {
|
||||
case MGCP_RTP_OSMUX:
|
||||
OSMO_STRBUF_PRINTF(sb, " CID=%u", conn->u.rtp.osmux.local_cid);
|
||||
OSMO_STRBUF_PRINTF(sb, " CID=%u", conn_rtp->osmux.local_cid);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < conn->u.rtp.end.codecs_assigned; i++) {
|
||||
struct mgcp_rtp_codec *c = &conn->u.rtp.end.codecs[i];
|
||||
OSMO_STRBUF_PRINTF(sb, " %s#%d%s", c->subtype_name, c->payload_type, c->fmtp);
|
||||
}
|
||||
|
||||
OSMO_STRBUF_PRINTF(sb, ")");
|
||||
break;
|
||||
|
||||
@@ -420,16 +361,6 @@ struct mgcp_conn *mgcp_find_dst_conn(struct mgcp_conn *conn)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! get oldest connection in the list.
|
||||
* \param[in] endp associated endpoint */
|
||||
struct mgcp_conn *mgcp_conn_get_oldest(struct mgcp_endpoint *endp)
|
||||
{
|
||||
if (llist_empty(&endp->conns))
|
||||
return NULL;
|
||||
|
||||
return llist_last_entry(&endp->conns, struct mgcp_conn, entry);
|
||||
}
|
||||
|
||||
const struct value_string mgcp_conn_rtp_type_names[] = {
|
||||
{ MGCP_RTP_DEFAULT, "rtp" },
|
||||
{ MGCP_RTP_OSMUX, "osmux" },
|
||||
|
||||
@@ -301,7 +301,6 @@ static void sync_frame_out_cb(void *user_data, const ubit_t *bits, unsigned int
|
||||
|
||||
mgcp_send(endp, 1, NULL, msg, &conn_dst->u.rtp, &conn_dst->u.rtp);
|
||||
|
||||
msgb_free(msg);
|
||||
return;
|
||||
skip:
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, E1_I460_TRAU_RX_FAIL_CTR));
|
||||
@@ -325,7 +324,7 @@ static void e1_send_ts_frame(struct e1inp_ts *ts, struct mgcp_trunk *trunk)
|
||||
msgb_length(msg) > DEBUG_BYTES_MAX ? DEBUG_BYTES_MAX : msgb_length(msg)));
|
||||
#endif
|
||||
/* Hand data over to the E1 stack */
|
||||
msgb_enqueue(&ts->raw.tx_queue, msg);
|
||||
e1inp_ts_send_raw(ts, msg);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -466,11 +465,21 @@ static enum osmo_trau_frame_type determine_trau_fr_type(char *sdp_subtype_name,
|
||||
{
|
||||
if (strcmp(sdp_subtype_name, "GSM") == 0)
|
||||
return OSMO_TRAU16_FT_FR;
|
||||
else if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
|
||||
|
||||
if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
|
||||
return OSMO_TRAU16_FT_EFR;
|
||||
else if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0)
|
||||
return OSMO_TRAU16_FT_HR;
|
||||
else if (strcmp(sdp_subtype_name, "AMR") == 0) {
|
||||
|
||||
if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0) {
|
||||
if (i460_rate == OSMO_I460_RATE_16k)
|
||||
return OSMO_TRAU16_FT_HR;
|
||||
if (i460_rate == OSMO_I460_RATE_8k)
|
||||
return OSMO_TRAU8_SPEECH;
|
||||
LOGPENDP(endp, DE1, LOGL_ERROR,
|
||||
"E1-TRAU-TX: unsupported or illegal I.460 rate for HR\n");
|
||||
return OSMO_TRAU_FT_NONE;
|
||||
}
|
||||
|
||||
if (strcmp(sdp_subtype_name, "AMR") == 0) {
|
||||
if (i460_rate == OSMO_I460_RATE_8k) {
|
||||
switch (amr_ft) {
|
||||
case AMR_4_75:
|
||||
@@ -488,11 +497,11 @@ static enum osmo_trau_frame_type determine_trau_fr_type(char *sdp_subtype_name,
|
||||
}
|
||||
}
|
||||
return OSMO_TRAU16_FT_AMR;
|
||||
} else {
|
||||
LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n",
|
||||
sdp_subtype_name);
|
||||
return OSMO_TRAU_FT_NONE;
|
||||
}
|
||||
|
||||
LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n",
|
||||
sdp_subtype_name);
|
||||
return OSMO_TRAU_FT_NONE;
|
||||
}
|
||||
|
||||
/* Determine a suitable TRAU frame type for a given codec */
|
||||
@@ -501,11 +510,21 @@ static enum osmo_tray_sync_pat_id determine_trau_sync_pat(char *sdp_subtype_name
|
||||
{
|
||||
if (strcmp(sdp_subtype_name, "GSM") == 0)
|
||||
return OSMO_TRAU_SYNCP_16_FR_EFR;
|
||||
else if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
|
||||
|
||||
if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
|
||||
return OSMO_TRAU_SYNCP_16_FR_EFR;
|
||||
else if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0)
|
||||
return OSMO_TRAU_SYNCP_8_HR;
|
||||
else if (strcmp(sdp_subtype_name, "AMR") == 0) {
|
||||
|
||||
if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0) {
|
||||
if (i460_rate == OSMO_I460_RATE_16k)
|
||||
return OSMO_TRAU_SYNCP_16_FR_EFR;
|
||||
if (i460_rate == OSMO_I460_RATE_8k)
|
||||
return OSMO_TRAU_SYNCP_8_HR;
|
||||
LOGPENDP(endp, DE1, LOGL_ERROR,
|
||||
"E1-TRAU-TX: unsupported or illegal I.460 rate for HR\n");
|
||||
return OSMO_TRAU_SYNCP_16_FR_EFR;
|
||||
}
|
||||
|
||||
if (strcmp(sdp_subtype_name, "AMR") == 0) {
|
||||
if (i460_rate == OSMO_I460_RATE_8k) {
|
||||
switch (amr_ft) {
|
||||
case AMR_4_75:
|
||||
@@ -523,11 +542,11 @@ static enum osmo_tray_sync_pat_id determine_trau_sync_pat(char *sdp_subtype_name
|
||||
}
|
||||
}
|
||||
return OSMO_TRAU_SYNCP_16_FR_EFR;
|
||||
} else {
|
||||
LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n",
|
||||
sdp_subtype_name);
|
||||
return OSMO_TRAU_SYNCP_16_FR_EFR;
|
||||
}
|
||||
|
||||
LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n",
|
||||
sdp_subtype_name);
|
||||
return OSMO_TRAU_SYNCP_16_FR_EFR;
|
||||
}
|
||||
|
||||
/* Find out if a given TRAU frame type is AMR */
|
||||
@@ -631,15 +650,17 @@ int mgcp_e1_endp_equip(struct mgcp_endpoint *endp, uint8_t ts, uint8_t ss, uint8
|
||||
void mgcp_e1_endp_update(struct mgcp_endpoint *endp)
|
||||
{
|
||||
struct mgcp_conn *conn;
|
||||
struct mgcp_conn_rtp *conn_rtp;
|
||||
struct mgcp_rtp_codec *codec;
|
||||
enum osmo_tray_sync_pat_id sync_pat_id;
|
||||
|
||||
/* In order to determine the codec, find the oldest connection on
|
||||
* the endpoint and use its codec information. Normally on an E1
|
||||
* endpoint no more than one connection should exist. */
|
||||
conn = mgcp_conn_get_oldest(endp);
|
||||
conn = mgcp_endp_get_conn_oldest(endp);
|
||||
OSMO_ASSERT(conn);
|
||||
codec = conn->u.rtp.end.codec;
|
||||
conn_rtp = mgcp_conn_get_conn_rtp(conn);
|
||||
codec = conn_rtp->end.codec;
|
||||
OSMO_ASSERT(codec);
|
||||
|
||||
/* Update codec information */
|
||||
|
||||
@@ -58,7 +58,7 @@ static char *gen_virtual_epname(void *ctx, const char *domain,
|
||||
}
|
||||
|
||||
/* Generate E1 endpoint name from given numeric parameters */
|
||||
static char *gen_e1_epname(void *ctx, const char *domain, unsigned int trunk_nr,
|
||||
static char *gen_e1_epname(const void *ctx, const char *domain, unsigned int trunk_nr,
|
||||
uint8_t ts_nr, uint8_t ss_nr)
|
||||
{
|
||||
unsigned int rate;
|
||||
@@ -446,7 +446,7 @@ static uint8_t e1_ss_nr_from_epname(const char *epname)
|
||||
/* Check if the selected E1 endpoint is avalable, which means that none of
|
||||
* the overlapping endpoints are currently serving a call. (if the system
|
||||
* is properly configured such a situation should never ocurr!) */
|
||||
static bool endp_avail_e1(struct mgcp_endpoint *endp)
|
||||
static bool endp_avail_e1(const struct mgcp_endpoint *endp)
|
||||
{
|
||||
/* The following map shows the overlapping of the subslots and their
|
||||
* respective rates. The numbers on the right running from top to bottom
|
||||
@@ -552,7 +552,7 @@ static bool endp_avail_e1(struct mgcp_endpoint *endp)
|
||||
/*! check if an endpoint is available for any kind of operation.
|
||||
* \param[in] endp endpoint to check.
|
||||
* \returns true if endpoint is avalable, false it is blocked for any reason. */
|
||||
bool mgcp_endp_avail(struct mgcp_endpoint *endp)
|
||||
bool mgcp_endp_avail(const struct mgcp_endpoint *endp)
|
||||
{
|
||||
switch (endp->trunk->trunk_type) {
|
||||
case MGCP_TRUNK_VIRTUAL:
|
||||
@@ -569,6 +569,24 @@ bool mgcp_endp_avail(struct mgcp_endpoint *endp)
|
||||
return false;
|
||||
}
|
||||
|
||||
/*! Get number of conns in an endpoint.
|
||||
* \param[in] endp endpoint to check.
|
||||
* \returns Number of connections present in the endpoint. */
|
||||
unsigned int mgcp_endp_num_conns(const struct mgcp_endpoint *endp)
|
||||
{
|
||||
return llist_count(&endp->conns);
|
||||
}
|
||||
|
||||
/*! check if an endpoint can in current state allocate new conns.
|
||||
* \param[in] endp endpoint to check.
|
||||
* \returns true if more connections can be allowed on endpoint, false if it is already busy. */
|
||||
bool mgcp_endp_is_full(const struct mgcp_endpoint *endp)
|
||||
{
|
||||
if (endp->type->max_conns == 0)
|
||||
return false;
|
||||
return mgcp_endp_num_conns(endp) >= endp->type->max_conns;
|
||||
}
|
||||
|
||||
/*! claim endpoint, sets callid and activates endpoint, should be called at the
|
||||
* beginning of the CRCX procedure when it is clear that a new call should be
|
||||
* created.
|
||||
@@ -655,6 +673,97 @@ void mgcp_endp_remove_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn)
|
||||
mgcp_endp_release(endp);
|
||||
}
|
||||
|
||||
/*! free oldest connection in the list.
|
||||
* \param[in] endp associated endpoint */
|
||||
void mgcp_endp_free_conn_oldest(struct mgcp_endpoint *endp)
|
||||
{
|
||||
struct mgcp_conn *conn;
|
||||
|
||||
if (llist_empty(&endp->conns))
|
||||
return;
|
||||
|
||||
conn = llist_last_entry(&endp->conns, struct mgcp_conn, entry);
|
||||
mgcp_conn_free(conn);
|
||||
}
|
||||
|
||||
/*! free all connections at once.
|
||||
* \param[in] endp associated endpoint */
|
||||
#if defined(__has_attribute)
|
||||
#if __has_attribute(no_sanitize)
|
||||
__attribute__((no_sanitize("undefined"))) /* ubsan detects a misaligned load */
|
||||
#endif
|
||||
#endif
|
||||
void mgcp_endp_free_conn_all(struct mgcp_endpoint *endp)
|
||||
{
|
||||
struct mgcp_conn *conn;
|
||||
|
||||
/* Drop all items in the list, might be consecutive! */
|
||||
while ((conn = llist_first_entry_or_null(&endp->conns, struct mgcp_conn, entry)))
|
||||
mgcp_conn_free(conn);
|
||||
}
|
||||
|
||||
/*! find a connection by its ID.
|
||||
* \param[in] endp associated endpoint
|
||||
* \param[in] id identification number of the connection
|
||||
* \returns pointer to allocated connection, NULL if not found */
|
||||
struct mgcp_conn *mgcp_endp_get_conn(struct mgcp_endpoint *endp, const char *id)
|
||||
{
|
||||
struct mgcp_conn *conn;
|
||||
const char *id_upper;
|
||||
const char *conn_id;
|
||||
|
||||
if (!id || !*id)
|
||||
return NULL;
|
||||
|
||||
/* Ignore leading zeros in needle */
|
||||
while (*id == '0')
|
||||
id++;
|
||||
|
||||
/* Use uppercase to compare identifiers, to avoid mismatches: RFC3435 2.1.3.2 "Names of
|
||||
* Connections" defines the id as a hex string, so clients may return lower case hex even though
|
||||
* we sent upper case hex in the CRCX response. */
|
||||
id_upper = osmo_str_toupper(id);
|
||||
|
||||
llist_for_each_entry(conn, &endp->conns, entry) {
|
||||
/* Ignore leading zeros in haystack */
|
||||
for (conn_id = conn->id; *conn_id == '0'; conn_id++);
|
||||
|
||||
if (strcmp(conn_id, id_upper) == 0)
|
||||
return conn;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! get oldest connection in the list.
|
||||
* \param[in] endp associated endpoint */
|
||||
struct mgcp_conn *mgcp_endp_get_conn_oldest(struct mgcp_endpoint *endp)
|
||||
{
|
||||
if (llist_empty(&endp->conns))
|
||||
return NULL;
|
||||
|
||||
return llist_last_entry(&endp->conns, struct mgcp_conn, entry);
|
||||
}
|
||||
|
||||
/*! find an RTP connection by its ID.
|
||||
* \param[in] endp associated endpoint
|
||||
* \param[in] id identification number of the connection
|
||||
* \returns pointer to allocated connection, NULL if not found */
|
||||
struct mgcp_conn_rtp *mgcp_endp_get_conn_rtp(struct mgcp_endpoint *endp,
|
||||
const char *id)
|
||||
{
|
||||
struct mgcp_conn *conn;
|
||||
|
||||
conn = mgcp_endp_get_conn(endp, id);
|
||||
if (!conn)
|
||||
return NULL;
|
||||
|
||||
if (conn->type == MGCP_CONN_TYPE_RTP)
|
||||
return mgcp_conn_get_conn_rtp(conn);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! release endpoint, all open connections are closed.
|
||||
* \param[in] endp endpoint to release */
|
||||
void mgcp_endp_release(struct mgcp_endpoint *endp)
|
||||
@@ -665,7 +774,7 @@ void mgcp_endp_release(struct mgcp_endpoint *endp)
|
||||
* all connections have been removed already. In case
|
||||
* that there are still connections open (e.g. when
|
||||
* RSIP is executed), free them all at once. */
|
||||
mgcp_conn_free_all(endp);
|
||||
mgcp_endp_free_conn_all(endp);
|
||||
|
||||
/* We must only decrement the stat item when the endpoint as actually
|
||||
* claimed. An endpoint is claimed when a call-id is set */
|
||||
|
||||
@@ -311,7 +311,6 @@ static int bridge_iuup_to_rtp_peer(struct mgcp_conn_rtp *conn_rtp_src, struct mg
|
||||
};
|
||||
|
||||
rc = mgcp_send(conn_rtp_dst->conn->endp, true, NULL, msg, conn_rtp_src, conn_rtp_dst);
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -469,7 +468,7 @@ static int mgcp_send_iuup(struct mgcp_endpoint *endp, struct msgb *msg,
|
||||
struct rtp_hdr *hdr = (struct rtp_hdr *)msgb_data(msg);
|
||||
int buflen = msgb_length(msg);
|
||||
char *dest_name;
|
||||
int len;
|
||||
int rc;
|
||||
|
||||
OSMO_ASSERT(conn_src);
|
||||
OSMO_ASSERT(conn_dst);
|
||||
@@ -506,6 +505,7 @@ static int mgcp_send_iuup(struct mgcp_endpoint *endp, struct msgb *msg,
|
||||
hdr->timestamp = osmo_htonl(mgcp_get_current_ts(rtp_end->codec->rate));
|
||||
hdr->sequence = osmo_htons(rtp_state->alt_rtp_tx_sequence);
|
||||
hdr->ssrc = rtp_state->alt_rtp_tx_ssrc;
|
||||
rtp_state->alt_rtp_tx_sequence++;
|
||||
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||
"process/send IuUP to %s %s rtp_port:%u rtcp_port:%u\n",
|
||||
@@ -513,19 +513,17 @@ static int mgcp_send_iuup(struct mgcp_endpoint *endp, struct msgb *msg,
|
||||
osmo_sockaddr_port(&rtp_end->addr.u.sa), ntohs(rtp_end->rtcp_port));
|
||||
|
||||
/* Forward a copy of the RTP data to a debug ip/port */
|
||||
forward_data_tap(rtp_end->rtp.fd, &conn_src->tap_out,
|
||||
msg);
|
||||
forward_data_tap(rtp_end->rtp, &conn_src->tap_out, msg);
|
||||
|
||||
len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr, (char *)hdr, buflen);
|
||||
rc = mgcp_udp_send(rtp_end->rtp, &rtp_end->addr, (char *)hdr, buflen);
|
||||
|
||||
if (len <= 0)
|
||||
return len;
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rtpconn_rate_ctr_add(conn_dst, endp, RTP_PACKETS_TX_CTR, 1);
|
||||
rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, len);
|
||||
rtp_state->alt_rtp_tx_sequence++;
|
||||
rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, buflen);
|
||||
|
||||
return len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Received TNL primitive from IuUP layer FSM, transmit it further down to the
|
||||
@@ -546,7 +544,7 @@ static int _conn_iuup_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
|
||||
|
||||
msgb_pull_to_l2(msg);
|
||||
rtph = (struct rtp_hdr *)msgb_push(msg, sizeof(*rtph));
|
||||
/* TODO: fill rtph properly: */
|
||||
/* rtph is further filled in mgcp_send_iuup() below. */
|
||||
*rtph = (struct rtp_hdr){
|
||||
.csrc_count = 0,
|
||||
.extension = 0,
|
||||
@@ -640,7 +638,7 @@ free_ret:
|
||||
}
|
||||
|
||||
/* Build IuUP RNL Data primitive from msg containing an incoming RTP pkt from
|
||||
* peer and send it down the IuUP layer towards the destination as IuUP/RTP: */
|
||||
* peer and send it down the IuUP layer towards the destination as IuUP/RTP. Takes ownership of msg. */
|
||||
int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn_rtp *conn_dest_rtp, struct msgb *msg)
|
||||
{
|
||||
struct osmo_iuup_rnl_prim *irp;
|
||||
|
||||
@@ -76,61 +76,24 @@ void mgcp_disp_msg(unsigned char *message, unsigned int len, char *preamble)
|
||||
|
||||
/*! Parse connection mode.
|
||||
* \param[in] mode as string (recvonly, sendrecv, sendonly confecho or loopback)
|
||||
* \param[in] endp pointer to endpoint (only used for log output)
|
||||
* \param[out] associated connection to be modified accordingly
|
||||
* \returns 0 on success, -1 on error */
|
||||
int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn *conn)
|
||||
* \returns MGCP_CONN_* on success, MGCP_CONN_NONE on error */
|
||||
enum mgcp_connection_mode mgcp_parse_conn_mode(const char *mode)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!mode) {
|
||||
LOGPCONN(conn, DLMGCP, LOGL_ERROR,
|
||||
"missing connection mode\n");
|
||||
return -1;
|
||||
}
|
||||
if (!conn)
|
||||
return -1;
|
||||
if (!endp)
|
||||
return -1;
|
||||
if (!mode)
|
||||
return MGCP_CONN_NONE;
|
||||
|
||||
if (strcasecmp(mode, "recvonly") == 0)
|
||||
conn->mode = MGCP_CONN_RECV_ONLY;
|
||||
else if (strcasecmp(mode, "sendrecv") == 0)
|
||||
conn->mode = MGCP_CONN_RECV_SEND;
|
||||
else if (strcasecmp(mode, "sendonly") == 0)
|
||||
conn->mode = MGCP_CONN_SEND_ONLY;
|
||||
else if (strcasecmp(mode, "confecho") == 0)
|
||||
conn->mode = MGCP_CONN_CONFECHO;
|
||||
else if (strcasecmp(mode, "loopback") == 0)
|
||||
conn->mode = MGCP_CONN_LOOPBACK;
|
||||
else {
|
||||
LOGPCONN(conn, DLMGCP, LOGL_ERROR,
|
||||
"unknown connection mode: '%s'\n", mode);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
/* Special handling for RTP connections */
|
||||
if (conn->type == MGCP_CONN_TYPE_RTP) {
|
||||
conn->u.rtp.end.output_enabled = !!(conn->mode & MGCP_CONN_SEND_ONLY);
|
||||
}
|
||||
|
||||
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "conn:%s\n", mgcp_conn_dump(conn));
|
||||
|
||||
LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "connection mode '%s' %d\n",
|
||||
mode, conn->mode);
|
||||
|
||||
/* Special handling für RTP connections */
|
||||
if (conn->type == MGCP_CONN_TYPE_RTP) {
|
||||
LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "output_enabled %u\n",
|
||||
conn->u.rtp.end.output_enabled);
|
||||
}
|
||||
|
||||
/* The VTY might change the connection mode at any time, so we have
|
||||
* to hold a copy of the original connection mode */
|
||||
conn->mode_orig = conn->mode;
|
||||
|
||||
return ret;
|
||||
return MGCP_CONN_RECV_ONLY;
|
||||
if (strcasecmp(mode, "sendrecv") == 0)
|
||||
return MGCP_CONN_RECV_SEND;
|
||||
if (strcasecmp(mode, "sendonly") == 0)
|
||||
return MGCP_CONN_SEND_ONLY;
|
||||
if (strcasecmp(mode, "confecho") == 0)
|
||||
return MGCP_CONN_CONFECHO;
|
||||
if (strcasecmp(mode, "loopback") == 0)
|
||||
return MGCP_CONN_LOOPBACK;
|
||||
return MGCP_CONN_NONE;
|
||||
}
|
||||
|
||||
/*! Analyze and parse the the hader of an MGCP messeage string.
|
||||
@@ -294,7 +257,7 @@ int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id)
|
||||
}
|
||||
|
||||
/* Check if connection exists */
|
||||
if (mgcp_conn_get(endp, conn_id))
|
||||
if (mgcp_endp_get_conn(endp, conn_id))
|
||||
return 0;
|
||||
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
/*
|
||||
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2009-2012 by On-Waves
|
||||
* (C) 2013-2024 by sysmocom - s.f.m.c. GmbH
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -70,14 +71,20 @@ void rtpconn_rate_ctr_inc(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *
|
||||
rtpconn_rate_ctr_add(conn_rtp, endp, id, 1);
|
||||
}
|
||||
|
||||
static int rx_rtp(struct msgb *msg);
|
||||
|
||||
bool mgcp_rtp_end_remote_addr_available(const struct mgcp_rtp_end *rtp_end)
|
||||
/* wrapper around libosmocore msgb_copy_c, which [at least before libosmocore.git Change-Id
|
||||
* I68328adb952ca8833ba047cb3b49ccc6f8a1f1b5] doesn't copy the cb */
|
||||
static inline struct msgb *mgw_msgb_copy_c(void *ctx, struct msgb *msg, const char *name)
|
||||
{
|
||||
return (osmo_sockaddr_port(&rtp_end->addr.u.sa) != 0) &&
|
||||
(osmo_sockaddr_is_any(&rtp_end->addr) == 0);
|
||||
struct msgb *msg2 = msgb_copy_c(ctx, msg, name);
|
||||
if (OSMO_UNLIKELY(!msg2))
|
||||
return NULL;
|
||||
|
||||
memcpy(msg2->cb, msg->cb, sizeof(msg2->cb));
|
||||
return msg2;
|
||||
}
|
||||
|
||||
static int rx_rtp(struct msgb *msg);
|
||||
|
||||
/*! Determine the local rtp bind IP-address.
|
||||
* \param[out] addr caller provided memory to store the resulting IP-Address.
|
||||
* \param[in] endp mgcp endpoint, that holds a copy of the VTY parameters.
|
||||
@@ -404,15 +411,12 @@ static int align_rtp_timestamp_offset(const struct mgcp_endpoint *endp,
|
||||
/*! dummy callback to disable transcoding (see also cfg->rtp_processing_cb).
|
||||
* \param[in] associated endpoint.
|
||||
* \param[in] destination RTP end.
|
||||
* \param[in,out] pointer to buffer with voice data.
|
||||
* \param[in] voice data length.
|
||||
* \param[in] maximum size of caller provided voice data buffer.
|
||||
* \param[in,out] msg message bufffer containing data. Function might change length.
|
||||
* \returns ignores input parameters, return always 0. */
|
||||
int mgcp_rtp_processing_default(struct mgcp_endpoint *endp,
|
||||
struct mgcp_rtp_end *dst_end,
|
||||
char *data, int *len, int buf_size)
|
||||
struct msgb *msg)
|
||||
{
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG, "transcoding disabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -785,16 +789,18 @@ static int amr_oa_check(char *data, int len)
|
||||
|
||||
/* Forward data to a debug tap. This is debug function that is intended for
|
||||
* debugging the voice traffic with tools like gstreamer */
|
||||
void forward_data_tap(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg)
|
||||
void forward_data_tap(struct osmo_io_fd *iofd, struct mgcp_rtp_tap *tap, struct msgb *msg)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!tap->enabled)
|
||||
return;
|
||||
|
||||
rc = sendto(fd, msgb_data(msg), msgb_length(msg), 0, (struct sockaddr *)&tap->forward,
|
||||
sizeof(tap->forward));
|
||||
struct msgb *msg2 = msgb_copy(msg, "RTP TAP Tx");
|
||||
if (!msg2)
|
||||
return;
|
||||
|
||||
rc = osmo_iofd_sendto_msgb(iofd, msg2, 0, &tap->forward);
|
||||
if (rc < 0)
|
||||
LOGP(DRTP, LOGL_ERROR,
|
||||
"Forwarding tapped (debug) voice data failed.\n");
|
||||
@@ -966,7 +972,7 @@ static int check_rtp(struct mgcp_conn_rtp *conn_src, struct msgb *msg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Dispatch msg bridged from the sister conn in the endpoint.
|
||||
/*! Dispatch msg bridged from the sister conn in the endpoint. Takes ownership of msgb.
|
||||
* \param[in] conn_dst The destination conn that should handle and transmit the content to
|
||||
* its peer outside MGW.
|
||||
* \param[in] msg msgb containing an RTP pkt received by the sister conn in the endpoint,
|
||||
@@ -988,8 +994,10 @@ static int mgcp_conn_rtp_dispatch_rtp(struct mgcp_conn_rtp *conn_dst, struct msg
|
||||
/* Before we try to deliver the packet, we check if the destination
|
||||
* port and IP-Address make sense at all. If not, we will be unable
|
||||
* to deliver the packet. */
|
||||
if (check_rtp_destin(conn_dst) != 0)
|
||||
if (check_rtp_destin(conn_dst) != 0) {
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Depending on the RTP connection type, deliver the RTP packet to the
|
||||
* destination connection. */
|
||||
@@ -1024,39 +1032,46 @@ static int mgcp_conn_rtp_dispatch_rtp(struct mgcp_conn_rtp *conn_dst, struct msg
|
||||
* be discarded, this should not happen, normally the MGCP type
|
||||
* should be properly set */
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR, "bad MGCP type -- data discarded!\n");
|
||||
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*! send udp packet.
|
||||
* \param[in] fd associated file descriptor.
|
||||
/*! send message buffer via udp socket. If it succeeds, it takes ownership of the msgb and internally calls
|
||||
* msgb_free() after the aynchronous sendto() completes. In case of error, the msgb is still owned by the
|
||||
* caller and must be free'd accordingly.
|
||||
* \param[in] iofd associated file descriptor.
|
||||
* \param[in] addr destination ip-address.
|
||||
* \param[in] msg message buffer that holds the data to be send.
|
||||
* \returns 0 in case of success (takes msgb ownership), -1 on error (doesn't take msgb ownership). */
|
||||
static int mgcp_udp_send_msg(struct osmo_io_fd *iofd, const struct osmo_sockaddr *addr, struct msgb *msg)
|
||||
{
|
||||
LOGP(DRTP, LOGL_DEBUG, "sending %d bytes length packet to %s ...\n", msgb_length(msg),
|
||||
osmo_sockaddr_to_str(addr));
|
||||
|
||||
return osmo_iofd_sendto_msgb(iofd, msg, 0, addr);
|
||||
}
|
||||
|
||||
/*! send udp packet from raw buffer/length.
|
||||
* \param[in] iofd associated file descriptor.
|
||||
* \param[in] addr destination ip-address.
|
||||
* \param[in] buf buffer that holds the data to be send.
|
||||
* \param[in] len length of the data to be sent.
|
||||
* \returns bytes sent, -1 on error. */
|
||||
int mgcp_udp_send(int fd, const struct osmo_sockaddr *addr, const char *buf, int len)
|
||||
* \returns 0 in case of success, -1 on error. */
|
||||
int mgcp_udp_send(struct osmo_io_fd *iofd, const struct osmo_sockaddr *addr, const char *buf, int len)
|
||||
{
|
||||
char ipbuf[INET6_ADDRSTRLEN];
|
||||
size_t addr_len;
|
||||
struct msgb *msg = msgb_alloc_c(iofd, len, "mgcp_udp_send");
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
memcpy(msg->tail, buf, len);
|
||||
msgb_put(msg, len);
|
||||
|
||||
LOGP(DRTP, LOGL_DEBUG,
|
||||
"sending %i bytes length packet to %s:%u ...\n", len,
|
||||
osmo_sockaddr_ntop(&addr->u.sa, ipbuf),
|
||||
osmo_sockaddr_port(&addr->u.sa));
|
||||
|
||||
if (addr->u.sa.sa_family == AF_INET6) {
|
||||
addr_len = sizeof(addr->u.sin6);
|
||||
} else {
|
||||
addr_len = sizeof(addr->u.sin);
|
||||
}
|
||||
|
||||
return sendto(fd, buf, len, 0, &addr->u.sa, addr_len);
|
||||
return mgcp_udp_send_msg(iofd, addr, msg);
|
||||
}
|
||||
|
||||
/*! send RTP dummy packet (to keep NAT connection open).
|
||||
* \param[in] endp mcgp endpoint that holds the RTP connection.
|
||||
* \param[in] conn associated RTP connection.
|
||||
* \returns bytes sent, -1 on error. */
|
||||
* \returns 0 in case of success, -1 on error. */
|
||||
int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
int rc;
|
||||
@@ -1078,8 +1093,7 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
||||
if (mgcp_conn_rtp_is_iuup(conn))
|
||||
rc = mgcp_conn_iuup_send_dummy(conn);
|
||||
else
|
||||
rc = mgcp_udp_send(conn->end.rtp.fd, &conn->end.addr,
|
||||
rtp_dummy_payload, sizeof(rtp_dummy_payload));
|
||||
rc = mgcp_udp_send(conn->end.rtp, &conn->end.addr, rtp_dummy_payload, sizeof(rtp_dummy_payload));
|
||||
|
||||
if (rc == -1)
|
||||
goto failed;
|
||||
@@ -1090,10 +1104,10 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
||||
was_rtcp = 1;
|
||||
rtcp_addr = conn->end.addr;
|
||||
osmo_sockaddr_set_port(&rtcp_addr.u.sa, ntohs(conn->end.rtcp_port));
|
||||
rc = mgcp_udp_send(conn->end.rtcp.fd, &rtcp_addr,
|
||||
rc = mgcp_udp_send(conn->end.rtcp, &rtcp_addr,
|
||||
rtp_dummy_payload, sizeof(rtp_dummy_payload));
|
||||
|
||||
if (rc >= 0)
|
||||
if (rc == 0)
|
||||
return rc;
|
||||
|
||||
failed:
|
||||
@@ -1104,7 +1118,7 @@ failed:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*! Send RTP/RTCP data to a specified destination connection.
|
||||
/*! Send RTP/RTCP data to a specified destination connection. Takes ownership of msg.
|
||||
* \param[in] endp associated endpoint (for configuration, logging).
|
||||
* \param[in] is_rtp flag to specify if the packet is of type RTP or RTCP.
|
||||
* \param[in] addr spoofed source address (set to NULL to disable).
|
||||
@@ -1143,6 +1157,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
|
||||
if (is_rtp && !mgcp_conn_rtp_is_iuup(conn_src)) {
|
||||
if (mgcp_patch_pt(conn_dst, msg) < 0) {
|
||||
LOGPENDP(endp, DRTP, LOGL_NOTICE, "unable to patch payload type RTP packet, discarding...\n");
|
||||
msgb_free(msg);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
@@ -1168,73 +1183,69 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
|
||||
osmo_sockaddr_port(&rtp_end->addr.u.sa), ntohs(rtp_end->rtcp_port)
|
||||
);
|
||||
} else if (is_rtp) {
|
||||
int cont;
|
||||
int nbytes = 0;
|
||||
int buflen = msgb_length(msg);
|
||||
|
||||
/* Make sure we have a valid RTP header, in cases where no RTP
|
||||
* header is present, we will generate one. */
|
||||
gen_rtp_header(msg, rtp_end, rtp_state);
|
||||
|
||||
do {
|
||||
/* Run transcoder */
|
||||
cont = endp->trunk->cfg->rtp_processing_cb(endp, rtp_end, (char *)msgb_data(msg), &buflen, RTP_BUF_SIZE);
|
||||
if (cont < 0)
|
||||
break;
|
||||
/* Run transcoder */
|
||||
rc = endp->trunk->cfg->rtp_processing_cb(endp, rtp_end, msg);
|
||||
if (rc < 0) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR, "Error %d during transcoding\n", rc);
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (addr)
|
||||
mgcp_patch_and_count(endp, rtp_state, rtp_end,
|
||||
addr, msg);
|
||||
if (addr)
|
||||
mgcp_patch_and_count(endp, rtp_state, rtp_end, addr, msg);
|
||||
|
||||
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)) {
|
||||
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",
|
||||
oa ? "octet-aligned" : "bandwidth-efficient");
|
||||
break;
|
||||
}
|
||||
} else if (rtp_end->rfc5993_hr_convert &&
|
||||
strcmp(conn_src->end.codec->subtype_name, "GSM-HR-08") == 0) {
|
||||
rc = rfc5993_hr_convert(endp, msg);
|
||||
if (rc < 0) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR, "Error while converting to GSM-HR-08\n");
|
||||
break;
|
||||
}
|
||||
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);
|
||||
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");
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
} else if (rtp_end->rfc5993_hr_convert &&
|
||||
strcmp(conn_src->end.codec->subtype_name, "GSM-HR-08") == 0) {
|
||||
rc = rfc5993_hr_convert(endp, msg);
|
||||
if (rc < 0) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR, "Error while converting to GSM-HR-08\n");
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||
"process/send to %s %s "
|
||||
"rtp_port:%u rtcp_port:%u\n",
|
||||
dest_name,
|
||||
osmo_sockaddr_ntop(&rtp_end->addr.u.sa, ipbuf),
|
||||
osmo_sockaddr_port(&rtp_end->addr.u.sa), ntohs(rtp_end->rtcp_port)
|
||||
);
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||
"process/send to %s %s "
|
||||
"rtp_port:%u rtcp_port:%u\n",
|
||||
dest_name,
|
||||
osmo_sockaddr_ntop(&rtp_end->addr.u.sa, ipbuf),
|
||||
osmo_sockaddr_port(&rtp_end->addr.u.sa), ntohs(rtp_end->rtcp_port)
|
||||
);
|
||||
|
||||
/* Forward a copy of the RTP data to a debug ip/port */
|
||||
forward_data_tap(rtp_end->rtp.fd, &conn_src->tap_out,
|
||||
msg);
|
||||
/* Forward a copy of the RTP data to a debug ip/port */
|
||||
forward_data_tap(rtp_end->rtp, &conn_src->tap_out, msg);
|
||||
|
||||
len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr,
|
||||
(char *)msgb_data(msg), msgb_length(msg));
|
||||
len = msgb_length(msg);
|
||||
|
||||
if (len <= 0)
|
||||
return len;
|
||||
rc = mgcp_udp_send_msg(rtp_end->rtp, &rtp_end->addr, msg);
|
||||
if (rc < 0) {
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rtpconn_rate_ctr_inc(conn_dst, endp, RTP_PACKETS_TX_CTR);
|
||||
rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, len);
|
||||
rtp_state->alt_rtp_tx_sequence++;
|
||||
rtpconn_rate_ctr_inc(conn_dst, endp, RTP_PACKETS_TX_CTR);
|
||||
rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, len);
|
||||
rtp_state->alt_rtp_tx_sequence++;
|
||||
|
||||
nbytes += len;
|
||||
buflen = cont;
|
||||
} while (buflen > 0);
|
||||
return nbytes;
|
||||
return 0;
|
||||
} else if (!trunk->omit_rtcp) {
|
||||
struct osmo_sockaddr rtcp_addr = rtp_end->addr;
|
||||
osmo_sockaddr_set_port(&rtcp_addr.u.sa, rtp_end->rtcp_port);
|
||||
osmo_sockaddr_set_port(&rtcp_addr.u.sa, ntohs(rtp_end->rtcp_port));
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||
"send to %s %s rtp_port:%u rtcp_port:%u\n",
|
||||
dest_name, osmo_sockaddr_ntop(&rtcp_addr.u.sa, ipbuf),
|
||||
@@ -1242,19 +1253,54 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
|
||||
osmo_sockaddr_port(&rtcp_addr.u.sa)
|
||||
);
|
||||
|
||||
len = mgcp_udp_send(rtp_end->rtcp.fd, &rtcp_addr,
|
||||
(char *)msgb_data(msg), msgb_length(msg));
|
||||
len = msgb_length(msg);
|
||||
|
||||
rc = mgcp_udp_send_msg(rtp_end->rtcp, &rtcp_addr, msg);
|
||||
if (rc < 0) {
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rtpconn_rate_ctr_inc(conn_dst, endp, RTP_PACKETS_TX_CTR);
|
||||
rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, len);
|
||||
rtp_state->alt_rtp_tx_sequence++;
|
||||
|
||||
return len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! determine if there's only a single recipient in endp for data received via conn_src.
|
||||
* The function returns NULL in case there is no recipient, or in case there are multiple recipients.
|
||||
* \param endp The MGCP endpoint whose connections to analyze
|
||||
* \param conn_src The source MGCP connection [which shall not count in results]
|
||||
* \returns recipient donnection if there is only one; NULL in case there are multiple */
|
||||
static struct mgcp_conn *rtpbridge_get_only_recipient(struct mgcp_endpoint *endp, struct mgcp_conn *conn_src)
|
||||
{
|
||||
struct mgcp_conn *conn_ret = NULL;
|
||||
struct mgcp_conn *conn_dst;
|
||||
|
||||
llist_for_each_entry(conn_dst, &endp->conns, entry) {
|
||||
if (conn_dst == conn_src)
|
||||
continue;
|
||||
switch (conn_dst->mode) {
|
||||
case MGCP_CONN_SEND_ONLY:
|
||||
case MGCP_CONN_RECV_SEND:
|
||||
case MGCP_CONN_CONFECHO:
|
||||
if (conn_ret)
|
||||
return NULL;
|
||||
conn_ret = conn_dst;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return conn_ret;
|
||||
}
|
||||
|
||||
/*! Dispatch incoming RTP packet to opposite RTP connection.
|
||||
* \param[in] msg Message buffer to bridge, coming from source connection.
|
||||
* msg shall contain "struct osmo_rtp_msg_ctx *" attached in
|
||||
@@ -1291,13 +1337,13 @@ int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
|
||||
* packets back to their origin. We will use the originating
|
||||
* address data from the UDP packet header to patch the
|
||||
* outgoing address in connection on the fly */
|
||||
if (osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa) == 0) {
|
||||
memcpy(&conn->u.rtp.end.addr, from_addr,
|
||||
sizeof(conn->u.rtp.end.addr));
|
||||
if (osmo_sockaddr_port(&conn_src->end.addr.u.sa) == 0) {
|
||||
memcpy(&conn_src->end.addr, from_addr,
|
||||
sizeof(conn_src->end.addr));
|
||||
LOG_CONN_RTP(conn_src, LOGL_NOTICE,
|
||||
"loopback mode: implicitly using source address (%s:%u) as destination address\n",
|
||||
osmo_sockaddr_ntop(&from_addr->u.sa, ipbuf),
|
||||
osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa));
|
||||
osmo_sockaddr_port(&conn_src->end.addr.u.sa));
|
||||
}
|
||||
return mgcp_conn_rtp_dispatch_rtp(conn_src, msg);
|
||||
}
|
||||
@@ -1312,23 +1358,44 @@ int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* If the mode is "confecho", send RTP back to the sender. */
|
||||
if (conn->mode == MGCP_CONN_CONFECHO)
|
||||
rc = mgcp_conn_rtp_dispatch_rtp(conn_src, msg);
|
||||
/* All the use cases above are 1:1 where we have one source msgb and we're sending that to one
|
||||
* destination. msgb ownership had been passed to the respective _*dospatch_rtp() function.
|
||||
* In the cases below, we actually [can] have multiple recipients, so we copy the original msgb
|
||||
* for each of the recipients. */
|
||||
|
||||
/* Dispatch RTP packet to all other connection(s) that send audio. */
|
||||
llist_for_each_entry(conn_dst, &endp->conns, entry) {
|
||||
if (conn_dst == conn)
|
||||
continue;
|
||||
switch (conn_dst->mode) {
|
||||
case MGCP_CONN_SEND_ONLY:
|
||||
case MGCP_CONN_RECV_SEND:
|
||||
case MGCP_CONN_CONFECHO:
|
||||
rc = mgcp_conn_rtp_dispatch_rtp(&conn_dst->u.rtp, msg);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
/* If the mode is "confecho", send RTP back to the sender. */
|
||||
if (conn->mode == MGCP_CONN_CONFECHO) {
|
||||
struct msgb *msg2 = mgw_msgb_copy_c(conn, msg, "RTP confecho");
|
||||
if (OSMO_LIKELY(msg2))
|
||||
rc = mgcp_conn_rtp_dispatch_rtp(conn_src, msg2);
|
||||
}
|
||||
|
||||
conn_dst = rtpbridge_get_only_recipient(endp, conn);
|
||||
if (OSMO_LIKELY(conn_dst)) {
|
||||
/* we only have a single recipient and cann hence send the original msgb without copying */
|
||||
rc = mgcp_conn_rtp_dispatch_rtp(&conn_dst->u.rtp, msg);
|
||||
} else {
|
||||
/* Dispatch RTP packet to all other connection(s) that send audio. */
|
||||
llist_for_each_entry(conn_dst, &endp->conns, entry) {
|
||||
struct msgb *msg2;
|
||||
if (conn_dst == conn)
|
||||
continue;
|
||||
switch (conn_dst->mode) {
|
||||
case MGCP_CONN_SEND_ONLY:
|
||||
case MGCP_CONN_RECV_SEND:
|
||||
case MGCP_CONN_CONFECHO:
|
||||
/* we have multiple recipients and must make copies for each recipient */
|
||||
msg2 = mgw_msgb_copy_c(conn_dst, msg, "RTP Tx copy");
|
||||
if (OSMO_LIKELY(msg2))
|
||||
rc = mgcp_conn_rtp_dispatch_rtp(&conn_dst->u.rtp, msg2);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* as we only sent copies in the previous llist_for_each_entry() loop, we must free the
|
||||
* original one */
|
||||
msgb_free(msg);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -1355,19 +1422,19 @@ int mgcp_dispatch_e1_bridge_cb(struct msgb *msg)
|
||||
* packets back to their origin. We will use the originating
|
||||
* address data from the UDP packet header to patch the
|
||||
* outgoing address in connection on the fly */
|
||||
if (osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa) == 0) {
|
||||
memcpy(&conn->u.rtp.end.addr, from_addr,
|
||||
sizeof(conn->u.rtp.end.addr));
|
||||
if (osmo_sockaddr_port(&conn_src->end.addr.u.sa) == 0) {
|
||||
memcpy(&conn_src->end.addr, from_addr,
|
||||
sizeof(conn_src->end.addr));
|
||||
LOG_CONN_RTP(conn_src, LOGL_NOTICE,
|
||||
"loopback mode: implicitly using source address (%s:%u) as destination address\n",
|
||||
osmo_sockaddr_ntop(&from_addr->u.sa, ipbuf),
|
||||
osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa));
|
||||
osmo_sockaddr_port(&conn_src->end.addr.u.sa));
|
||||
}
|
||||
return mgcp_conn_rtp_dispatch_rtp(conn_src, msg);
|
||||
}
|
||||
|
||||
/* Forward to E1 */
|
||||
return mgcp_e1_send_rtp(conn->endp, conn->u.rtp.end.codec, msg);
|
||||
return mgcp_e1_send_rtp(conn->endp, conn_src->end.codec, msg);
|
||||
}
|
||||
|
||||
/*! cleanup an endpoint when a connection on an RTP bridge endpoint is removed.
|
||||
@@ -1400,7 +1467,7 @@ void mgcp_cleanup_e1_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *con
|
||||
}
|
||||
|
||||
/* Handle incoming RTP data from NET */
|
||||
static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
|
||||
static void rtp_recvfrom_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg, const struct osmo_sockaddr *saddr)
|
||||
{
|
||||
/* NOTE: This is a generic implementation. RTP data is received. In
|
||||
* case of loopback the data is just sent back to its origin. All
|
||||
@@ -1411,49 +1478,34 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
|
||||
|
||||
struct mgcp_conn_rtp *conn_src;
|
||||
struct mgcp_endpoint *endp;
|
||||
struct osmo_sockaddr addr;
|
||||
socklen_t slen = sizeof(addr);
|
||||
char ipbuf[INET6_ADDRSTRLEN];
|
||||
int ret;
|
||||
enum rtp_proto proto;
|
||||
struct osmo_rtp_msg_ctx *mc;
|
||||
struct msgb *msg;
|
||||
int rc;
|
||||
|
||||
conn_src = (struct mgcp_conn_rtp *)fd->data;
|
||||
conn_src = (struct mgcp_conn_rtp *) osmo_iofd_get_data(iofd);
|
||||
OSMO_ASSERT(conn_src);
|
||||
endp = conn_src->conn->endp;
|
||||
OSMO_ASSERT(endp);
|
||||
msg = msgb_alloc_c(endp->trunk, RTP_BUF_SIZE, "RTP-rx");
|
||||
|
||||
proto = (fd == &conn_src->end.rtp)? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
|
||||
proto = (iofd == conn_src->end.rtp) ? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
|
||||
|
||||
ret = recvfrom(fd->fd, msgb_data(msg), msg->data_len, 0, (struct sockaddr *)&addr.u.sa, &slen);
|
||||
|
||||
if (ret <= 0) {
|
||||
LOG_CONN_RTP(conn_src, LOGL_ERROR, "recvfrom error: %s\n", strerror(errno));
|
||||
rc = -1;
|
||||
goto out;
|
||||
if (res <= 0) {
|
||||
LOG_CONN_RTP(conn_src, LOGL_ERROR, "recvfrom error: %s\n", strerror(-res));
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
msgb_put(msg, ret);
|
||||
|
||||
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "%s: rx %u bytes from %s:%u\n",
|
||||
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "%s: rx %u bytes from %s\n",
|
||||
proto == MGCP_PROTO_RTP ? "RTP" : "RTCP",
|
||||
msgb_length(msg), osmo_sockaddr_ntop(&addr.u.sa, ipbuf),
|
||||
osmo_sockaddr_port(&addr.u.sa));
|
||||
msgb_length(msg), osmo_sockaddr_to_str(saddr));
|
||||
|
||||
if ((proto == MGCP_PROTO_RTP && check_rtp(conn_src, msg))
|
||||
|| (proto == MGCP_PROTO_RTCP && check_rtcp(conn_src, msg))) {
|
||||
/* Logging happened in the two check_ functions */
|
||||
rc = -1;
|
||||
goto out;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (mgcp_is_rtp_dummy_payload(msg)) {
|
||||
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "rx dummy packet (dropped)\n");
|
||||
rc = 0;
|
||||
goto out;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
/* Since the msgb remains owned and freed by this function, the msg ctx data struct can just be on the stack and
|
||||
@@ -1462,7 +1514,7 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
|
||||
*mc = (struct osmo_rtp_msg_ctx){
|
||||
.proto = proto,
|
||||
.conn_src = conn_src,
|
||||
.from_addr = &addr,
|
||||
.from_addr = (struct osmo_sockaddr *) saddr,
|
||||
};
|
||||
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "msg ctx: %d %p %s\n",
|
||||
mc->proto, mc->conn_src,
|
||||
@@ -1477,16 +1529,17 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
|
||||
/* FIXME: count RTP and RTCP separately, also count IuUP payload-less separately */
|
||||
|
||||
/* Forward a copy of the RTP data to a debug ip/port */
|
||||
forward_data_tap(fd->fd, &conn_src->tap_in, msg);
|
||||
forward_data_tap(iofd, &conn_src->tap_in, msg);
|
||||
|
||||
rc = rx_rtp(msg);
|
||||
rx_rtp(msg);
|
||||
return;
|
||||
|
||||
out:
|
||||
out_free:
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Note: This function is able to handle RTP and RTCP */
|
||||
/* Note: This function is able to handle RTP and RTCP. msgb ownership is transferred, so this function or its
|
||||
* downstream consumers must make sure to [eventually] free the msgb. */
|
||||
static int rx_rtp(struct msgb *msg)
|
||||
{
|
||||
struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg);
|
||||
@@ -1499,25 +1552,23 @@ static int rx_rtp(struct msgb *msg)
|
||||
|
||||
/* Check if the origin of the RTP packet seems plausible */
|
||||
if (!trunk->rtp_accept_all && check_rtp_origin(conn_src, from_addr))
|
||||
return -1;
|
||||
goto out_free;
|
||||
|
||||
/* Handle AMR frame format conversion (octet-aligned vs. bandwith-efficient) */
|
||||
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)
|
||||
return -1;
|
||||
src_oa = mgcp_codec_amr_is_octet_aligned(conn_src->end.codec);
|
||||
if (((bool)oa) != src_oa) {
|
||||
goto out_free;
|
||||
if (((bool)oa) != conn_src->end.codec->param.amr_octet_aligned) {
|
||||
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), src_oa, oa);
|
||||
return -1;
|
||||
msgb_length(msg), conn_src->end.codec->param.amr_octet_aligned, oa);
|
||||
goto out_free;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1526,17 +1577,36 @@ static int rx_rtp(struct msgb *msg)
|
||||
/* Execute endpoint specific implementation that handles the
|
||||
* dispatching of the RTP data */
|
||||
return conn->endp->type->dispatch_rtp_cb(msg);
|
||||
out_free:
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void rtp_sendto_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg, const struct osmo_sockaddr *daddr)
|
||||
{
|
||||
/* nothing; osmo_io takes care of msgb_free */
|
||||
if (res < 0) {
|
||||
struct mgcp_conn_rtp *conn_rtp = (struct mgcp_conn_rtp *) osmo_iofd_get_data(iofd);
|
||||
int priv_nr = osmo_iofd_get_priv_nr(iofd);
|
||||
char errbuf[129];
|
||||
strerror_r(-res, errbuf, sizeof(errbuf));
|
||||
LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "%s sendto(%s) failed: %s\n", priv_nr ? "RTCP" : "RTP",
|
||||
osmo_sockaddr_to_str(daddr), errbuf);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct osmo_io_ops rtp_ioops = {
|
||||
.recvfrom_cb = rtp_recvfrom_cb,
|
||||
.sendto_cb = rtp_sendto_cb,
|
||||
};
|
||||
|
||||
/*! bind RTP port to osmo_fd.
|
||||
* \param[in] source_addr source (local) address to bind on.
|
||||
* \param[in] fd associated file descriptor.
|
||||
* \param[in] port to bind on.
|
||||
* \param[in] dscp IP DSCP value to use.
|
||||
* \param[in] prio socket priority to use.
|
||||
* \returns 0 on success, -1 on ERROR. */
|
||||
int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port, uint8_t dscp,
|
||||
uint8_t prio)
|
||||
* \returns file descriptor on success, -1 on ERROR. */
|
||||
int mgcp_create_bind(const char *source_addr, int port, uint8_t dscp, uint8_t prio)
|
||||
{
|
||||
int rc;
|
||||
|
||||
@@ -1544,47 +1614,50 @@ int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port, uint
|
||||
NULL, 0, OSMO_SOCK_F_BIND | OSMO_SOCK_F_DSCP(dscp) |
|
||||
OSMO_SOCK_F_PRIO(prio));
|
||||
if (rc < 0) {
|
||||
LOGP(DRTP, LOGL_ERROR, "failed to bind UDP port (%s:%i).\n",
|
||||
LOGP(DRTP, LOGL_ERROR, "failed to bind UDP port (%s:%d).\n",
|
||||
source_addr, port);
|
||||
return -1;
|
||||
}
|
||||
fd->fd = rc;
|
||||
LOGP(DRTP, LOGL_DEBUG, "created socket + bound UDP port (%s:%i).\n", source_addr, port);
|
||||
LOGP(DRTP, LOGL_DEBUG, "created socket + bound UDP port (%s:%d).\n", source_addr, port);
|
||||
|
||||
return 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Bind RTP and RTCP port (helper function for mgcp_bind_net_rtp_port()) */
|
||||
static int bind_rtp(struct mgcp_config *cfg, const char *source_addr,
|
||||
struct mgcp_rtp_end *rtp_end, struct mgcp_endpoint *endp)
|
||||
{
|
||||
int rc, rtp_fd, rtcp_fd;
|
||||
|
||||
/* NOTE: The port that is used for RTCP is the RTP port incremented by one
|
||||
* (e.g. RTP-Port = 16000 ==> RTCP-Port = 16001) */
|
||||
|
||||
if (mgcp_create_bind(source_addr, &rtp_end->rtp, rtp_end->local_port,
|
||||
cfg->endp_dscp, cfg->endp_priority) != 0) {
|
||||
rc = mgcp_create_bind(source_addr, rtp_end->local_port, cfg->endp_dscp, cfg->endp_priority);
|
||||
if (rc < 0) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR,
|
||||
"failed to create RTP port: %s:%d\n",
|
||||
source_addr, rtp_end->local_port);
|
||||
goto cleanup0;
|
||||
}
|
||||
rtp_fd = rc;
|
||||
|
||||
if (mgcp_create_bind(source_addr, &rtp_end->rtcp, rtp_end->local_port + 1,
|
||||
cfg->endp_dscp, cfg->endp_priority) != 0) {
|
||||
rc = mgcp_create_bind(source_addr, rtp_end->local_port + 1, cfg->endp_dscp, cfg->endp_priority);
|
||||
if (rc < 0) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR,
|
||||
"failed to create RTCP port: %s:%d\n",
|
||||
source_addr, rtp_end->local_port + 1);
|
||||
goto cleanup1;
|
||||
}
|
||||
rtcp_fd = rc;
|
||||
|
||||
if (osmo_fd_register(&rtp_end->rtp) != 0) {
|
||||
if (osmo_iofd_register(rtp_end->rtp, rtp_fd) < 0) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR,
|
||||
"failed to register RTP port %d\n",
|
||||
rtp_end->local_port);
|
||||
goto cleanup2;
|
||||
}
|
||||
|
||||
if (osmo_fd_register(&rtp_end->rtcp) != 0) {
|
||||
if (osmo_iofd_register(rtp_end->rtcp, rtcp_fd) != 0) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR,
|
||||
"failed to register RTCP port %d\n",
|
||||
rtp_end->local_port + 1);
|
||||
@@ -1594,13 +1667,11 @@ static int bind_rtp(struct mgcp_config *cfg, const char *source_addr,
|
||||
return 0;
|
||||
|
||||
cleanup3:
|
||||
osmo_fd_unregister(&rtp_end->rtp);
|
||||
osmo_iofd_unregister(rtp_end->rtp);
|
||||
cleanup2:
|
||||
close(rtp_end->rtcp.fd);
|
||||
rtp_end->rtcp.fd = -1;
|
||||
close(rtcp_fd);
|
||||
cleanup1:
|
||||
close(rtp_end->rtp.fd);
|
||||
rtp_end->rtp.fd = -1;
|
||||
close(rtp_fd);
|
||||
cleanup0:
|
||||
return -1;
|
||||
}
|
||||
@@ -1619,7 +1690,8 @@ int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
|
||||
snprintf(name, sizeof(name), "%s-%s", conn->conn->name, conn->conn->id);
|
||||
end = &conn->end;
|
||||
|
||||
if (end->rtp.fd != -1 || end->rtcp.fd != -1) {
|
||||
if ((end->rtp && osmo_iofd_get_fd(end->rtp) != -1) ||
|
||||
(end->rtcp && osmo_iofd_get_fd(end->rtcp) != -1)) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR, "%u was already bound on conn:%s\n",
|
||||
rtp_port, mgcp_conn_dump(conn->conn));
|
||||
|
||||
@@ -1632,25 +1704,43 @@ int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
|
||||
}
|
||||
|
||||
end->local_port = rtp_port;
|
||||
osmo_fd_setup(&end->rtp, -1, OSMO_FD_READ, rtp_data_net, conn, 0);
|
||||
osmo_fd_setup(&end->rtcp, -1, OSMO_FD_READ, rtp_data_net, conn, 0);
|
||||
end->rtp = osmo_iofd_setup(conn->conn, -1, name, OSMO_IO_FD_MODE_RECVFROM_SENDTO, &rtp_ioops, conn);
|
||||
if (!end->rtp)
|
||||
return -EIO;
|
||||
osmo_iofd_set_alloc_info(end->rtp, RTP_BUF_SIZE, 0);
|
||||
end->rtcp = osmo_iofd_setup(conn->conn, -1, name, OSMO_IO_FD_MODE_RECVFROM_SENDTO, &rtp_ioops, conn);
|
||||
if (!end->rtcp) {
|
||||
osmo_iofd_free(end->rtp);
|
||||
end->rtp = NULL;
|
||||
return -EIO;
|
||||
}
|
||||
osmo_iofd_set_alloc_info(end->rtcp, RTP_BUF_SIZE, 0);
|
||||
osmo_iofd_set_priv_nr(end->rtcp, 1); /* we use priv_nr as identifier for RTCP */
|
||||
|
||||
return bind_rtp(endp->trunk->cfg, conn->end.local_addr, end, endp);
|
||||
}
|
||||
|
||||
/***********************
|
||||
* mgcp_rtp_end
|
||||
**********************/
|
||||
|
||||
bool mgcp_rtp_end_remote_addr_available(const struct mgcp_rtp_end *rtp_end)
|
||||
{
|
||||
return (osmo_sockaddr_port(&rtp_end->addr.u.sa) != 0) &&
|
||||
(osmo_sockaddr_is_any(&rtp_end->addr) == 0);
|
||||
}
|
||||
|
||||
/*! free allocated RTP and RTCP ports.
|
||||
* \param[in] end RTP end */
|
||||
void mgcp_free_rtp_port(struct mgcp_rtp_end *end)
|
||||
void mgcp_rtp_end_free_port(struct mgcp_rtp_end *end)
|
||||
{
|
||||
if (end->rtp.fd != -1) {
|
||||
osmo_fd_unregister(&end->rtp);
|
||||
close(end->rtp.fd);
|
||||
end->rtp.fd = -1;
|
||||
if (end->rtp) {
|
||||
osmo_iofd_free(end->rtp);
|
||||
end->rtp = NULL;
|
||||
}
|
||||
|
||||
if (end->rtcp.fd != -1) {
|
||||
osmo_fd_unregister(&end->rtcp);
|
||||
close(end->rtcp.fd);
|
||||
end->rtcp.fd = -1;
|
||||
if (end->rtcp) {
|
||||
osmo_iofd_free(end->rtcp);
|
||||
end->rtcp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* (C) 2012-2013 by Pablo Neira Ayuso <pablo@gnumonks.org>
|
||||
* (C) 2012-2013 by On Waves ehf <http://www.on-waves.com>
|
||||
* (C) 2013-2024 by sysmocom - s.f.m.c. GmbH
|
||||
* All rights not specifically granted under this license are reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -13,9 +14,11 @@
|
||||
#include <string.h> /* for memcpy */
|
||||
#include <stdlib.h> /* for abs */
|
||||
#include <inttypes.h> /* for PRIu64 */
|
||||
#include <unistd.h> /* for PRIu64 */
|
||||
#include <netinet/in.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/osmo_io.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
|
||||
#include <osmocom/netif/osmux.h>
|
||||
@@ -30,8 +33,8 @@
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_trunk.h>
|
||||
|
||||
static struct osmo_fd osmux_fd_v4;
|
||||
static struct osmo_fd osmux_fd_v6;
|
||||
static struct osmo_io_fd *osmux_fd_v4;
|
||||
static struct osmo_io_fd *osmux_fd_v6;
|
||||
|
||||
static LLIST_HEAD(osmux_handle_list);
|
||||
|
||||
@@ -76,34 +79,31 @@ static void rtpconn_osmux_rate_ctr_inc(struct mgcp_conn_rtp *conn_rtp, int id)
|
||||
static void osmux_deliver_cb(struct msgb *batch_msg, void *data)
|
||||
{
|
||||
struct osmux_handle *handle = data;
|
||||
socklen_t dest_len;
|
||||
int rc, fd;
|
||||
struct mgcp_trunk *trunk = (struct mgcp_trunk *)osmux_fd_v4.data;
|
||||
int rc;
|
||||
struct osmo_io_fd *iofd;
|
||||
struct mgcp_trunk *trunk = (struct mgcp_trunk *) osmo_iofd_get_data(osmux_fd_v4);
|
||||
struct rate_ctr_group *all_osmux_stats = trunk->ratectr.all_osmux_conn_stats;
|
||||
|
||||
switch (handle->rem_addr.u.sa.sa_family) {
|
||||
case AF_INET6:
|
||||
dest_len = sizeof(handle->rem_addr.u.sin6);
|
||||
fd = osmux_fd_v6.fd;
|
||||
iofd = osmux_fd_v6;
|
||||
break;
|
||||
case AF_INET:
|
||||
default:
|
||||
dest_len = sizeof(handle->rem_addr.u.sin);
|
||||
fd = osmux_fd_v4.fd;
|
||||
iofd = osmux_fd_v4;
|
||||
break;
|
||||
}
|
||||
rc = sendto(fd, batch_msg->data, batch_msg->len, 0,
|
||||
(struct sockaddr *)&handle->rem_addr.u.sa, dest_len);
|
||||
rc = osmo_iofd_sendto_msgb(iofd, batch_msg, 0, &handle->rem_addr);
|
||||
if (rc < 0) {
|
||||
char errbuf[129];
|
||||
strerror_r(errno, errbuf, sizeof(errbuf));
|
||||
strerror_r(-rc, errbuf, sizeof(errbuf));
|
||||
LOGP(DOSMUX, LOGL_NOTICE, "osmux sendto(%s) failed: %s\n",
|
||||
osmo_sockaddr_to_str(&handle->rem_addr), errbuf);
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(all_osmux_stats, OSMUX_DROPPED_PACKETS_CTR));
|
||||
msgb_free(batch_msg);
|
||||
} else {
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(all_osmux_stats, OSMUX_PACKETS_TX_CTR));
|
||||
}
|
||||
msgb_free(batch_msg);
|
||||
}
|
||||
|
||||
/* Lookup existing OSMUX handle for specified destination address. */
|
||||
@@ -204,17 +204,17 @@ osmux_handle_find_or_create(const struct mgcp_trunk *trunk, const struct osmo_so
|
||||
return h->in;
|
||||
}
|
||||
|
||||
/*! send RTP packet through OSMUX connection.
|
||||
/*! send RTP packet through OSMUX connection. Takes ownership of msg.
|
||||
* \param[in] conn associated RTP connection
|
||||
* \param[in] msg msgb containing an RTP AMR packet
|
||||
* \returns 0 on success, -1 on ERROR */
|
||||
int conn_osmux_send_rtp(struct mgcp_conn_rtp *conn, struct msgb *msg)
|
||||
{
|
||||
int ret;
|
||||
struct msgb *msg2;
|
||||
|
||||
if (!conn->end.output_enabled) {
|
||||
rtpconn_osmux_rate_ctr_inc(conn, OSMUX_RTP_PACKETS_TX_DROPPED_CTR);
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -222,22 +222,19 @@ int conn_osmux_send_rtp(struct mgcp_conn_rtp *conn, struct msgb *msg)
|
||||
LOGPCONN(conn->conn, DOSMUX, LOGL_INFO, "forwarding RTP to Osmux conn not yet enabled, dropping (cid=%d)\n",
|
||||
conn->osmux.remote_cid);
|
||||
rtpconn_osmux_rate_ctr_inc(conn, OSMUX_RTP_PACKETS_TX_DROPPED_CTR);
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* msg is not owned by us and will be freed by the caller stack upon return: */
|
||||
msg2 = msgb_copy_c(conn->conn, msg, "osmux-rtp-send");
|
||||
if (!msg2)
|
||||
return -1;
|
||||
|
||||
/* Osmux implementation works with AMR OA only, make sure we convert to it if needed: */
|
||||
if (amr_oa_bwe_convert(conn->conn->endp, msg2, true) < 0) {
|
||||
if (amr_oa_bwe_convert(conn->conn->endp, msg, true) < 0) {
|
||||
LOGPCONN(conn->conn, DOSMUX, LOGL_ERROR,
|
||||
"Error converting to AMR octet-aligned mode\n");
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while ((ret = osmux_xfrm_input(conn->osmux.in, msg2, conn->osmux.remote_cid)) > 0) {
|
||||
while ((ret = osmux_xfrm_input(conn->osmux.in, msg, conn->osmux.remote_cid)) > 0) {
|
||||
/* batch full, build and deliver it */
|
||||
osmux_xfrm_input_deliver(conn->osmux.in);
|
||||
}
|
||||
@@ -245,7 +242,7 @@ int conn_osmux_send_rtp(struct mgcp_conn_rtp *conn, struct msgb *msg)
|
||||
rtpconn_osmux_rate_ctr_inc(conn, OSMUX_RTP_PACKETS_TX_DROPPED_CTR);
|
||||
} else {
|
||||
rtpconn_osmux_rate_ctr_inc(conn, OSMUX_RTP_PACKETS_TX_CTR);
|
||||
rtpconn_osmux_rate_ctr_add(conn, OSMUX_AMR_OCTETS_TX_CTR, msgb_length(msg2) - sizeof(struct rtp_hdr));
|
||||
rtpconn_osmux_rate_ctr_add(conn, OSMUX_AMR_OCTETS_TX_CTR, msgb_length(msg) - sizeof(struct rtp_hdr));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -268,7 +265,7 @@ osmux_conn_lookup(const struct mgcp_trunk *trunk, uint8_t local_cid, const struc
|
||||
if (conn->type != MGCP_CONN_TYPE_RTP)
|
||||
continue;
|
||||
|
||||
conn_rtp = &conn->u.rtp;
|
||||
conn_rtp = mgcp_conn_get_conn_rtp(conn);
|
||||
if (!mgcp_conn_rtp_is_osmux(conn_rtp))
|
||||
continue;
|
||||
|
||||
@@ -325,29 +322,7 @@ static void scheduled_from_osmux_tx_rtp_cb(struct msgb *msg, void *data)
|
||||
};
|
||||
|
||||
endp->type->dispatch_rtp_cb(msg);
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
static struct msgb *osmux_recv(struct osmo_fd *ofd, struct osmo_sockaddr *addr)
|
||||
{
|
||||
struct msgb *msg;
|
||||
socklen_t slen = sizeof(addr->u.sas);
|
||||
int ret;
|
||||
|
||||
msg = msgb_alloc(4096, "OSMUX");
|
||||
if (!msg) {
|
||||
LOGP(DOSMUX, LOGL_ERROR, "cannot allocate message\n");
|
||||
return NULL;
|
||||
}
|
||||
ret = recvfrom(ofd->fd, msg->data, msg->data_len, 0, &addr->u.sa, &slen);
|
||||
if (ret <= 0) {
|
||||
msgb_free(msg);
|
||||
LOGP(DOSMUX, LOGL_ERROR, "cannot receive message\n");
|
||||
return NULL;
|
||||
}
|
||||
msgb_put(msg, ret);
|
||||
|
||||
return msg;
|
||||
/* dispatch_rtp_cb() has taken ownership of the msgb */
|
||||
}
|
||||
|
||||
/* To be called every time some AMR data is received on a connection
|
||||
@@ -445,22 +420,16 @@ out:
|
||||
}
|
||||
|
||||
#define osmux_chunk_length(msg, rem) ((rem) - (msg)->len)
|
||||
static int osmux_read_fd_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
static void osmux_recvfrom_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg, const struct osmo_sockaddr *rem_addr)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct osmux_hdr *osmuxh;
|
||||
struct osmo_sockaddr rem_addr;
|
||||
uint32_t rem;
|
||||
struct mgcp_trunk *trunk = ofd->data;
|
||||
struct mgcp_trunk *trunk = osmo_iofd_get_data(iofd);
|
||||
struct rate_ctr_group *all_rtp_stats = trunk->ratectr.all_osmux_conn_stats;
|
||||
uint32_t rem;
|
||||
char addr_str[64];
|
||||
|
||||
msg = osmux_recv(ofd, &rem_addr);
|
||||
if (!msg)
|
||||
return -1;
|
||||
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(all_rtp_stats, OSMUX_PACKETS_RX_CTR));
|
||||
osmo_sockaddr_to_str_buf(addr_str, sizeof(addr_str), &rem_addr);
|
||||
osmo_sockaddr_to_str_buf(addr_str, sizeof(addr_str), rem_addr);
|
||||
|
||||
if (trunk->cfg->osmux.usage == OSMUX_USAGE_OFF) {
|
||||
LOGP(DOSMUX, LOGL_ERROR,
|
||||
@@ -470,14 +439,16 @@ static int osmux_read_fd_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
}
|
||||
|
||||
/* Catch legacy dummy message and process them separately: */
|
||||
if (msg->len == 2 && msg->data[0] == MGCP_DUMMY_LOAD)
|
||||
return osmux_handle_legacy_dummy(trunk, &rem_addr, msg);
|
||||
if (msg->len == 2 && msg->data[0] == MGCP_DUMMY_LOAD) {
|
||||
osmux_handle_legacy_dummy(trunk, rem_addr, msg);
|
||||
return;
|
||||
}
|
||||
|
||||
rem = msg->len;
|
||||
while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) {
|
||||
struct mgcp_conn_rtp *conn_src;
|
||||
conn_src = osmux_conn_lookup(trunk, osmuxh->circuit_id,
|
||||
&rem_addr);
|
||||
rem_addr);
|
||||
if (!conn_src) {
|
||||
LOGP(DOSMUX, LOGL_DEBUG,
|
||||
"Cannot find a src conn for %s CID=%d\n",
|
||||
@@ -485,7 +456,7 @@ static int osmux_read_fd_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
goto next;
|
||||
}
|
||||
|
||||
if (conn_osmux_event_data_received(conn_src, &rem_addr) < 0)
|
||||
if (conn_osmux_event_data_received(conn_src, rem_addr) < 0)
|
||||
goto next;
|
||||
|
||||
mgcp_conn_watchdog_kick(conn_src->conn);
|
||||
@@ -499,58 +470,94 @@ next:
|
||||
}
|
||||
out:
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void osmux_sendto_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg, const struct osmo_sockaddr *rem_addr)
|
||||
{
|
||||
/* nothing; osmo_io takes care of msgb_free */
|
||||
if (res < 0) {
|
||||
struct mgcp_trunk *trunk = (struct mgcp_trunk *) osmo_iofd_get_data(iofd);
|
||||
struct rate_ctr_group *all_osmux_stats = trunk->ratectr.all_osmux_conn_stats;
|
||||
char errbuf[129];
|
||||
strerror_r(-res, errbuf, sizeof(errbuf));
|
||||
LOGP(DOSMUX, LOGL_NOTICE, "osmux sendto(%s) failed: %s\n", osmo_sockaddr_to_str(rem_addr), errbuf);
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(all_osmux_stats, OSMUX_DROPPED_PACKETS_CTR));
|
||||
}
|
||||
}
|
||||
|
||||
static const struct osmo_io_ops osmux_ioops = {
|
||||
.recvfrom_cb = osmux_recvfrom_cb,
|
||||
.sendto_cb = osmux_sendto_cb,
|
||||
};
|
||||
|
||||
int osmux_init(struct mgcp_trunk *trunk)
|
||||
{
|
||||
int ret;
|
||||
int ret, fd;
|
||||
struct mgcp_config *cfg = trunk->cfg;
|
||||
|
||||
/* So far we only support running on one trunk: */
|
||||
OSMO_ASSERT(trunk == mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID));
|
||||
|
||||
osmo_fd_setup(&osmux_fd_v4, -1, OSMO_FD_READ, osmux_read_fd_cb, trunk, 0);
|
||||
osmo_fd_setup(&osmux_fd_v6, -1, OSMO_FD_READ, osmux_read_fd_cb, trunk, 0);
|
||||
osmux_fd_v4 = osmo_iofd_setup(trunk, -1, "osmux_fd_v4", OSMO_IO_FD_MODE_RECVFROM_SENDTO, &osmux_ioops, trunk);
|
||||
if (!osmux_fd_v4)
|
||||
goto out;
|
||||
osmo_iofd_set_alloc_info(osmux_fd_v4, 4096, 0);
|
||||
|
||||
if (cfg->osmux.local_addr_v4) {
|
||||
ret = mgcp_create_bind(cfg->osmux.local_addr_v4, &osmux_fd_v4, cfg->osmux.local_port,
|
||||
ret = mgcp_create_bind(cfg->osmux.local_addr_v4, cfg->osmux.local_port,
|
||||
cfg->endp_dscp, cfg->endp_priority);
|
||||
if (ret < 0) {
|
||||
LOGP(DOSMUX, LOGL_ERROR, "Cannot bind OSMUX IPv4 socket to %s:%u\n",
|
||||
cfg->osmux.local_addr_v4, cfg->osmux.local_port);
|
||||
return ret;
|
||||
goto out_free_v4;
|
||||
}
|
||||
fd = ret;
|
||||
|
||||
ret = osmo_fd_register(&osmux_fd_v4);
|
||||
ret = osmo_iofd_register(osmux_fd_v4, fd);
|
||||
if (ret < 0) {
|
||||
LOGP(DOSMUX, LOGL_ERROR, "Cannot register OSMUX IPv4 socket %s\n",
|
||||
osmo_sock_get_name2(osmux_fd_v4.fd));
|
||||
return ret;
|
||||
LOGP(DOSMUX, LOGL_ERROR, "Cannot register OSMUX IPv4 socket %s\n", osmo_sock_get_name2(fd));
|
||||
close(fd);
|
||||
goto out_free_v4;
|
||||
}
|
||||
LOGP(DOSMUX, LOGL_INFO, "OSMUX IPv4 socket listening on %s\n",
|
||||
osmo_sock_get_name2(osmux_fd_v4.fd));
|
||||
LOGP(DOSMUX, LOGL_INFO, "OSMUX IPv4 socket listening on %s\n", osmo_sock_get_name2(fd));
|
||||
}
|
||||
|
||||
osmux_fd_v6 = osmo_iofd_setup(trunk, -1, "osmux_fd_v6", OSMO_IO_FD_MODE_RECVFROM_SENDTO, &osmux_ioops, trunk);
|
||||
if (!osmux_fd_v6)
|
||||
goto out_free_v4;
|
||||
osmo_iofd_set_alloc_info(osmux_fd_v6, 4096, 0);
|
||||
|
||||
if (cfg->osmux.local_addr_v6) {
|
||||
ret = mgcp_create_bind(cfg->osmux.local_addr_v6, &osmux_fd_v6, cfg->osmux.local_port,
|
||||
ret = mgcp_create_bind(cfg->osmux.local_addr_v6, cfg->osmux.local_port,
|
||||
cfg->endp_dscp, cfg->endp_priority);
|
||||
if (ret < 0) {
|
||||
LOGP(DOSMUX, LOGL_ERROR, "Cannot bind OSMUX IPv6 socket to [%s]:%u\n",
|
||||
cfg->osmux.local_addr_v6, cfg->osmux.local_port);
|
||||
return ret;
|
||||
goto out_free_v6;
|
||||
}
|
||||
fd = ret;
|
||||
|
||||
ret = osmo_fd_register(&osmux_fd_v6);
|
||||
ret = osmo_iofd_register(osmux_fd_v6, fd);
|
||||
if (ret < 0) {
|
||||
LOGP(DOSMUX, LOGL_ERROR, "Cannot register OSMUX IPv6 socket %s\n",
|
||||
osmo_sock_get_name2(osmux_fd_v6.fd));
|
||||
return ret;
|
||||
LOGP(DOSMUX, LOGL_ERROR, "Cannot register OSMUX IPv6 socket %s\n", osmo_sock_get_name2(fd));
|
||||
close(fd);
|
||||
goto out_free_v6;
|
||||
}
|
||||
LOGP(DOSMUX, LOGL_INFO, "OSMUX IPv6 socket listening on %s\n",
|
||||
osmo_sock_get_name2(osmux_fd_v6.fd));
|
||||
LOGP(DOSMUX, LOGL_INFO, "OSMUX IPv6 socket listening on %s\n", osmo_sock_get_name2(fd));
|
||||
}
|
||||
cfg->osmux.initialized = true;
|
||||
return 0;
|
||||
|
||||
out_free_v6:
|
||||
/* osmo_iofd_free performs unregister + close */
|
||||
osmo_iofd_free(osmux_fd_v6);
|
||||
osmux_fd_v6 = NULL;
|
||||
out_free_v4:
|
||||
/* osmo_iofd_free performs unregister + close */
|
||||
osmo_iofd_free(osmux_fd_v4);
|
||||
osmux_fd_v4 = NULL;
|
||||
out:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*! relase OSXMUX cid, that had been allocated to this connection.
|
||||
@@ -688,7 +695,7 @@ void conn_osmux_disable(struct mgcp_conn_rtp *conn)
|
||||
|
||||
/*! send RTP dummy packet to OSMUX connection port.
|
||||
* \param[in] conn associated RTP connection
|
||||
* \returns bytes sent, -1 on error */
|
||||
* \returns 0 in case of success, -1 on error */
|
||||
int osmux_send_dummy(struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
char ipbuf[INET6_ADDRSTRLEN];
|
||||
@@ -716,7 +723,7 @@ int osmux_send_dummy(struct mgcp_conn_rtp *conn)
|
||||
osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf),
|
||||
osmo_sockaddr_port(&conn->end.addr.u.sa), conn->osmux.remote_cid);
|
||||
|
||||
return mgcp_udp_send(osmux_fd_v4.fd, &conn->end.addr, (char *)osmuxh, buf_len);
|
||||
return mgcp_udp_send(osmux_fd_v4, &conn->end.addr, (char *)osmuxh, buf_len);
|
||||
}
|
||||
|
||||
/* Keeps track of locally allocated Osmux circuit ID. +7 to round up to 8 bit boundary. */
|
||||
|
||||
@@ -59,6 +59,16 @@ char *mgcp_debug_get_last_endpoint_name(void)
|
||||
return debug_last_endpoint_name;
|
||||
}
|
||||
|
||||
const struct value_string mgcp_connection_mode_strs[] = {
|
||||
{ MGCP_CONN_NONE, "none" },
|
||||
{ MGCP_CONN_RECV_SEND, "sendrecv" },
|
||||
{ MGCP_CONN_SEND_ONLY, "sendonly" },
|
||||
{ MGCP_CONN_RECV_ONLY, "recvonly" },
|
||||
{ MGCP_CONN_CONFECHO, "confecho" },
|
||||
{ MGCP_CONN_LOOPBACK, "loopback" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
/* A combination of LOGPENDP and LOGPTRUNK that automatically falls back to
|
||||
* LOGPTRUNK when the endp parameter is NULL */
|
||||
#define LOGPEPTR(endp, trunk, cat, level, fmt, args...) \
|
||||
@@ -172,7 +182,7 @@ static int setup_rtp_processing(struct mgcp_endpoint *endp,
|
||||
/* Find the "sister" connection */
|
||||
llist_for_each_entry(_conn, &endp->conns, entry) {
|
||||
if (_conn->id != conn->conn->id) {
|
||||
conn_src = &_conn->u.rtp;
|
||||
conn_src = mgcp_conn_get_conn_rtp(_conn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -397,23 +407,6 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
|
||||
rq.null_endp = mgcp_endp_is_null(pdata.epname);
|
||||
if (!rq.null_endp)
|
||||
rq.endp = mgcp_endp_by_name(&rc, pdata.epname, pdata.cfg);
|
||||
if (rq.endp) {
|
||||
struct mgcp_conn *c;
|
||||
int count = 0;
|
||||
llist_for_each_entry(c, &rq.endp->conns, entry) {
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
"%s: endp=%s conn %s\n",
|
||||
rq.name,
|
||||
rq.endp->name,
|
||||
mgcp_conn_dump(c));
|
||||
count++;
|
||||
}
|
||||
if (!count)
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
"%s: endp=%s no conns\n",
|
||||
rq.name,
|
||||
rq.endp->name);
|
||||
}
|
||||
rq.mgcp_cause = rc;
|
||||
if (!rq.endp) {
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_NO_ENDPOINT));
|
||||
@@ -446,7 +439,6 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
|
||||
/* Check if we have to retransmit a response from a previous transaction */
|
||||
if (pdata.trans && rq.endp->last_trans && strcmp(rq.endp->last_trans, pdata.trans) == 0) {
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_RETRANSMITTED));
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "%s: retransmission\n", rq.name);
|
||||
return create_retransmission_response(rq.endp);
|
||||
}
|
||||
}
|
||||
@@ -735,7 +727,7 @@ void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
|
||||
{
|
||||
struct mgcp_trunk *trunk = endp->trunk;
|
||||
|
||||
int patch_ssrc = expect_ssrc_change && trunk->force_constant_ssrc;
|
||||
bool patch_ssrc = expect_ssrc_change && trunk->force_constant_ssrc;
|
||||
|
||||
rtp->force_aligned_timing = trunk->force_aligned_timing;
|
||||
rtp->force_constant_ssrc = patch_ssrc ? 1 : 0;
|
||||
@@ -837,7 +829,7 @@ static int handle_codec_info(struct mgcp_conn_rtp *conn,
|
||||
/* Try to find an destination RTP connection that we can include in the codec decision. */
|
||||
conn_dst = mgcp_find_dst_conn(conn->conn);
|
||||
if (conn_dst && conn_dst->type == MGCP_CONN_TYPE_RTP)
|
||||
conn_dst_rtp = &conn_dst->u.rtp;
|
||||
conn_dst_rtp = mgcp_conn_get_conn_rtp(conn_dst);
|
||||
else
|
||||
conn_dst_rtp = NULL;
|
||||
|
||||
@@ -888,11 +880,11 @@ static struct msgb *handle_create_con(struct mgcp_request_data *rq)
|
||||
int error_code = 400;
|
||||
const char *local_options = NULL;
|
||||
const char *callid = NULL;
|
||||
const char *mode = NULL;
|
||||
enum mgcp_connection_mode mode = MGCP_CONN_NONE;
|
||||
char *line;
|
||||
int have_sdp = 0, remote_osmux_cid = -2;
|
||||
struct mgcp_conn_rtp *conn = NULL;
|
||||
struct mgcp_conn *_conn = NULL;
|
||||
struct mgcp_conn *conn = NULL;
|
||||
struct mgcp_conn_rtp *conn_rtp = NULL;
|
||||
char conn_name[512];
|
||||
int rc;
|
||||
|
||||
@@ -940,7 +932,7 @@ static struct msgb *handle_create_con(struct mgcp_request_data *rq)
|
||||
return create_err_response(rq->trunk, NULL, 523, "CRCX", pdata->trans);
|
||||
break;
|
||||
case 'M':
|
||||
mode = (const char *)line + 3;
|
||||
mode = mgcp_parse_conn_mode((const char *)line + 3);
|
||||
break;
|
||||
case 'X':
|
||||
if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
|
||||
@@ -977,15 +969,15 @@ mgcp_header_done:
|
||||
return create_err_response(endp, endp, 516, "CRCX", pdata->trans);
|
||||
}
|
||||
|
||||
if (!mode) {
|
||||
if (mode == MGCP_CONN_NONE) {
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"CRCX: insufficient parameters, missing mode\n");
|
||||
"CRCX: insufficient parameters, invalid mode\n");
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE));
|
||||
return create_err_response(endp, endp, 517, "CRCX", pdata->trans);
|
||||
}
|
||||
|
||||
/* Check if we are able to accept the creation of another connection */
|
||||
if (endp->type->max_conns > 0 && llist_count(&endp->conns) >= endp->type->max_conns) {
|
||||
if (mgcp_endp_is_full(endp)) {
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"CRCX: endpoint full, max. %i connections allowed!\n",
|
||||
endp->type->max_conns);
|
||||
@@ -993,7 +985,8 @@ mgcp_header_done:
|
||||
/* There is no more room for a connection, make some
|
||||
* room by blindly tossing the oldest of the two two
|
||||
* connections */
|
||||
mgcp_conn_free_oldest(endp);
|
||||
mgcp_endp_free_conn_oldest(endp);
|
||||
OSMO_ASSERT(!mgcp_endp_is_full(endp));
|
||||
} else {
|
||||
/* There is no more room for a connection, leave
|
||||
* everything as it is and return with an error */
|
||||
@@ -1032,36 +1025,35 @@ mgcp_header_done:
|
||||
}
|
||||
|
||||
snprintf(conn_name, sizeof(conn_name), "%s", callid);
|
||||
_conn = mgcp_conn_alloc(trunk->endpoints, endp, MGCP_CONN_TYPE_RTP, conn_name);
|
||||
if (!_conn) {
|
||||
conn = mgcp_conn_alloc(trunk->endpoints, endp, MGCP_CONN_TYPE_RTP, conn_name);
|
||||
if (!conn) {
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"CRCX: unable to allocate RTP connection\n");
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_ALLOC_CONN));
|
||||
goto error2;
|
||||
|
||||
}
|
||||
|
||||
conn = mgcp_conn_get_rtp(endp, _conn->id);
|
||||
OSMO_ASSERT(conn);
|
||||
|
||||
if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
|
||||
if (mgcp_conn_set_mode(conn, mode) < 0) {
|
||||
error_code = 517;
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE));
|
||||
goto error2;
|
||||
}
|
||||
|
||||
conn_rtp = mgcp_conn_get_conn_rtp(conn);
|
||||
OSMO_ASSERT(conn_rtp);
|
||||
|
||||
/* If X-Osmux (remote CID) was received (-1 is wilcard), alloc next avail CID as local CID */
|
||||
if (remote_osmux_cid >= -1) {
|
||||
if (osmux_init_conn(conn) < 0) {
|
||||
if (osmux_init_conn(conn_rtp) < 0) {
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
|
||||
goto error2;
|
||||
}
|
||||
if (remote_osmux_cid >= 0) {
|
||||
conn->osmux.remote_cid_present = true;
|
||||
conn->osmux.remote_cid = remote_osmux_cid;
|
||||
conn_rtp->osmux.remote_cid_present = true;
|
||||
conn_rtp->osmux.remote_cid = remote_osmux_cid;
|
||||
}
|
||||
} else if (endp->trunk->cfg->osmux.usage == OSMUX_USAGE_ONLY) {
|
||||
LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
|
||||
LOGPCONN(conn, DLMGCP, LOGL_ERROR,
|
||||
"CRCX: osmux only and no osmux offered\n");
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
|
||||
goto error2;
|
||||
@@ -1072,7 +1064,7 @@ mgcp_header_done:
|
||||
rc = set_local_cx_options(trunk->endpoints,
|
||||
&endp->local_options, local_options);
|
||||
if (rc != 0) {
|
||||
LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
|
||||
LOGPCONN(conn, DLMGCP, LOGL_ERROR,
|
||||
"CRCX: invalid local connection options!\n");
|
||||
error_code = rc;
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS));
|
||||
@@ -1081,8 +1073,8 @@ mgcp_header_done:
|
||||
}
|
||||
|
||||
/* Handle codec information and decide for a suitable codec */
|
||||
rc = handle_codec_info(conn, rq, have_sdp, true);
|
||||
mgcp_codec_summary(conn);
|
||||
rc = handle_codec_info(conn_rtp, rq, have_sdp, true);
|
||||
mgcp_codec_summary(conn_rtp);
|
||||
if (rc) {
|
||||
error_code = rc;
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CODEC_NEGOTIATION));
|
||||
@@ -1090,72 +1082,60 @@ mgcp_header_done:
|
||||
}
|
||||
/* Upgrade the conn type RTP_DEFAULT->RTP_IUUP if needed based on requested codec: */
|
||||
/* TODO: "codec" probably needs to be moved from endp to conn */
|
||||
if (conn->type == MGCP_RTP_DEFAULT && strcmp(conn->end.codec->subtype_name, "VND.3GPP.IUFP") == 0) {
|
||||
rc = mgcp_conn_iuup_init(conn);
|
||||
if (conn_rtp->type == MGCP_RTP_DEFAULT && strcmp(conn_rtp->end.codec->subtype_name, "VND.3GPP.IUFP") == 0) {
|
||||
rc = mgcp_conn_iuup_init(conn_rtp);
|
||||
}
|
||||
|
||||
if (pdata->cfg->force_ptime) {
|
||||
conn->end.packet_duration_ms = pdata->cfg->force_ptime;
|
||||
conn->end.force_output_ptime = 1;
|
||||
conn_rtp->end.packet_duration_ms = pdata->cfg->force_ptime;
|
||||
conn_rtp->end.force_output_ptime = 1;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
mgcp_rtp_end_config(endp, 0, &conn_rtp->end);
|
||||
|
||||
/* 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) {
|
||||
if (mgcp_get_local_addr(conn_rtp->end.local_addr, conn_rtp) < 0) {
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
|
||||
goto error2;
|
||||
}
|
||||
if (allocate_port(endp, conn) != 0) {
|
||||
if (allocate_port(endp, conn_rtp) != 0) {
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
|
||||
goto error2;
|
||||
}
|
||||
|
||||
if (setup_rtp_processing(endp, conn) != 0) {
|
||||
LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
|
||||
if (setup_rtp_processing(endp, conn_rtp) != 0) {
|
||||
LOGPCONN(conn, DLMGCP, LOGL_ERROR,
|
||||
"CRCX: could not start RTP processing!\n");
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_START_RTP));
|
||||
goto error2;
|
||||
}
|
||||
|
||||
/* Notify Osmux conn that CRCX was received */
|
||||
if (mgcp_conn_rtp_is_osmux(conn)) {
|
||||
if (conn_osmux_event_rx_crcx_mdcx(conn) < 0) {
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, "CRCX: Osmux handling failed!\n");
|
||||
if (mgcp_conn_rtp_is_osmux(conn_rtp)) {
|
||||
if (conn_osmux_event_rx_crcx_mdcx(conn_rtp) < 0) {
|
||||
LOGPCONN(conn, DLMGCP, LOGL_ERROR, "CRCX: Osmux handling failed!\n");
|
||||
goto error2;
|
||||
}
|
||||
}
|
||||
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
|
||||
"CRCX: Creating connection: port: %u\n", conn->end.local_port);
|
||||
LOGPCONN(conn, DLMGCP, LOGL_DEBUG,
|
||||
"CRCX: Creating connection: port: %u\n", conn_rtp->end.local_port);
|
||||
|
||||
/* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
|
||||
OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
|
||||
if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
|
||||
if (conn->mode & MGCP_CONN_RECV_ONLY &&
|
||||
trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
|
||||
send_dummy(endp, conn);
|
||||
send_dummy(endp, conn_rtp);
|
||||
|
||||
LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
|
||||
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);
|
||||
|
||||
/* NOTE: Only in the virtual trunk we allow dynamic endpoint names */
|
||||
bool add_epname = rq->wildcarded && trunk->trunk_type == MGCP_TRUNK_VIRTUAL;
|
||||
return create_response_with_sdp(endp, conn, "CRCX", pdata->trans, add_epname, true);
|
||||
return create_response_with_sdp(endp, conn_rtp, "CRCX", pdata->trans, add_epname, true);
|
||||
error2:
|
||||
mgcp_endp_release(endp);
|
||||
LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
|
||||
@@ -1172,13 +1152,13 @@ static struct msgb *handle_modify_con(struct mgcp_request_data *rq)
|
||||
struct rate_ctr_group *rate_ctrs;
|
||||
char new_local_addr[INET6_ADDRSTRLEN];
|
||||
int error_code = 500;
|
||||
int silent = 0;
|
||||
int have_sdp = 0;
|
||||
char *line;
|
||||
const char *local_options = NULL;
|
||||
const char *mode = NULL;
|
||||
struct mgcp_conn_rtp *conn = NULL;
|
||||
const char *conn_id = NULL;
|
||||
enum mgcp_connection_mode mode = MGCP_CONN_NONE;
|
||||
struct mgcp_conn *conn = NULL;
|
||||
struct mgcp_conn_rtp *conn_rtp = NULL;
|
||||
const char *conn_id = NULL;
|
||||
int remote_osmux_cid = -2;
|
||||
int rc;
|
||||
|
||||
@@ -1205,7 +1185,7 @@ static struct msgb *handle_modify_con(struct mgcp_request_data *rq)
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR, "MDCX: selected endpoint not available!\n");
|
||||
return create_err_response(rq->trunk, NULL, 501, "MDCX", pdata->trans);
|
||||
}
|
||||
if (llist_count(&endp->conns) <= 0) {
|
||||
if (mgcp_endp_num_conns(endp) <= 0) {
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"MDCX: endpoint is not holding a connection.\n");
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONN));
|
||||
@@ -1235,10 +1215,7 @@ static struct msgb *handle_modify_con(struct mgcp_request_data *rq)
|
||||
local_options = (const char *)line + 3;
|
||||
break;
|
||||
case 'M':
|
||||
mode = (const char *)line + 3;
|
||||
break;
|
||||
case 'Z':
|
||||
silent = strcasecmp("noanswer", line + 3) == 0;
|
||||
mode = mgcp_parse_conn_mode((const char *)line + 3);
|
||||
break;
|
||||
case 'X':
|
||||
if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
|
||||
@@ -1272,29 +1249,30 @@ mgcp_header_done:
|
||||
return create_err_response(endp, endp, 515, "MDCX", pdata->trans);
|
||||
}
|
||||
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
conn = mgcp_endp_get_conn(endp, conn_id);
|
||||
if (!conn) {
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_CONN_NOT_FOUND));
|
||||
return create_err_response(endp, endp, 400, "MDCX", pdata->trans);
|
||||
}
|
||||
|
||||
mgcp_conn_watchdog_kick(conn->conn);
|
||||
mgcp_conn_watchdog_kick(conn);
|
||||
|
||||
if (mode) {
|
||||
if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
|
||||
if (mode != MGCP_CONN_NONE) {
|
||||
if (mgcp_conn_set_mode(conn, mode) < 0) {
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_MODE));
|
||||
error_code = 517;
|
||||
goto error3;
|
||||
}
|
||||
} else
|
||||
conn->conn->mode = conn->conn->mode_orig;
|
||||
} else {
|
||||
conn->mode = conn->mode_orig;
|
||||
}
|
||||
|
||||
/* Set local connection options, if present */
|
||||
if (local_options) {
|
||||
rc = set_local_cx_options(trunk->endpoints,
|
||||
&endp->local_options, local_options);
|
||||
if (rc != 0) {
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
|
||||
LOGPCONN(conn, DLMGCP, LOGL_ERROR,
|
||||
"MDCX: invalid local connection options!\n");
|
||||
error_code = rc;
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS));
|
||||
@@ -1302,49 +1280,41 @@ mgcp_header_done:
|
||||
}
|
||||
}
|
||||
|
||||
conn_rtp = mgcp_conn_get_conn_rtp(conn);
|
||||
OSMO_ASSERT(conn_rtp);
|
||||
|
||||
/* Handle codec information and decide for a suitable codec */
|
||||
rc = handle_codec_info(conn, rq, have_sdp, false);
|
||||
mgcp_codec_summary(conn);
|
||||
rc = handle_codec_info(conn_rtp, rq, have_sdp, false);
|
||||
mgcp_codec_summary(conn_rtp);
|
||||
if (rc) {
|
||||
error_code = rc;
|
||||
goto error3;
|
||||
}
|
||||
/* Upgrade the conn type RTP_DEFAULT->RTP_IUUP if needed based on requested codec: */
|
||||
/* TODO: "codec" probably needs to be moved from endp to conn */
|
||||
if (conn->type == MGCP_RTP_DEFAULT && strcmp(conn->end.codec->subtype_name, "VND.3GPP.IUFP") == 0)
|
||||
rc = mgcp_conn_iuup_init(conn);
|
||||
if (conn_rtp->type == MGCP_RTP_DEFAULT && strcmp(conn_rtp->end.codec->subtype_name, "VND.3GPP.IUFP") == 0)
|
||||
rc = mgcp_conn_iuup_init(conn_rtp);
|
||||
|
||||
/* 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 (mgcp_conn_rtp_is_osmux(conn_rtp)) {
|
||||
OSMO_ASSERT(conn_rtp->osmux.local_cid_allocated);
|
||||
if (remote_osmux_cid < -1) {
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
|
||||
LOGPCONN(conn, DLMGCP, LOGL_ERROR,
|
||||
"MDCX: Failed to parse Osmux CID!\n");
|
||||
goto error3;
|
||||
} else if (remote_osmux_cid == -1) {
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
|
||||
LOGPCONN(conn, DLMGCP, LOGL_ERROR,
|
||||
"MDCX: wilcard in MDCX is not supported!\n");
|
||||
goto error3;
|
||||
} else if (conn->osmux.remote_cid_present &&
|
||||
remote_osmux_cid != conn->osmux.remote_cid) {
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
|
||||
"MDCX: changing already allocated CID is not supported!\n");
|
||||
goto error3;
|
||||
} else if (conn_rtp->osmux.remote_cid_present &&
|
||||
remote_osmux_cid != conn_rtp->osmux.remote_cid) {
|
||||
LOGPCONN(conn, DLMGCP, LOGL_ERROR,
|
||||
"MDCX: changing already allocated CID is not supported!\n");
|
||||
goto error3;
|
||||
} else {
|
||||
conn->osmux.remote_cid_present = true;
|
||||
conn->osmux.remote_cid = remote_osmux_cid;
|
||||
if (conn_osmux_event_rx_crcx_mdcx(conn) < 0) {
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
|
||||
conn_rtp->osmux.remote_cid_present = true;
|
||||
conn_rtp->osmux.remote_cid = remote_osmux_cid;
|
||||
if (conn_osmux_event_rx_crcx_mdcx(conn_rtp) < 0) {
|
||||
LOGPCONN(conn, DLMGCP, LOGL_ERROR,
|
||||
"MDCX: Osmux handling failed!\n");
|
||||
goto error3;
|
||||
}
|
||||
@@ -1355,50 +1325,44 @@ mgcp_header_done:
|
||||
to update our announced IP addr and re-bind our local end. This can
|
||||
happen for instance if MGW initially provided an IPv4 during CRCX
|
||||
ACK, and now MDCX tells us the remote has an IPv6 address. */
|
||||
if (mgcp_get_local_addr(new_local_addr, conn) < 0) {
|
||||
if (mgcp_get_local_addr(new_local_addr, conn_rtp) < 0) {
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
|
||||
goto error3;
|
||||
}
|
||||
if (strcmp(new_local_addr, conn->end.local_addr)) {
|
||||
osmo_strlcpy(conn->end.local_addr, new_local_addr, sizeof(conn->end.local_addr));
|
||||
mgcp_free_rtp_port(&conn->end);
|
||||
if (allocate_port(endp, conn) != 0) {
|
||||
if (strcmp(new_local_addr, conn_rtp->end.local_addr)) {
|
||||
osmo_strlcpy(conn_rtp->end.local_addr, new_local_addr, sizeof(conn_rtp->end.local_addr));
|
||||
mgcp_rtp_end_free_port(&conn_rtp->end);
|
||||
if (allocate_port(endp, conn_rtp) != 0) {
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
|
||||
goto error3;
|
||||
}
|
||||
}
|
||||
|
||||
if (setup_rtp_processing(endp, conn) != 0) {
|
||||
if (setup_rtp_processing(endp, conn_rtp) != 0) {
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_START_RTP));
|
||||
goto error3;
|
||||
}
|
||||
|
||||
mgcp_rtp_end_config(endp, 1, &conn->end);
|
||||
mgcp_rtp_end_config(endp, 1, &conn_rtp->end);
|
||||
|
||||
/* modify */
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
|
||||
"MDCX: modified conn:%s\n", mgcp_conn_dump(conn->conn));
|
||||
LOGPCONN(conn, DLMGCP, LOGL_DEBUG,
|
||||
"MDCX: modified conn:%s\n", mgcp_conn_dump(conn));
|
||||
|
||||
/* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
|
||||
OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
|
||||
if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
|
||||
if (conn->mode & MGCP_CONN_RECV_ONLY &&
|
||||
trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
|
||||
send_dummy(endp, conn);
|
||||
send_dummy(endp, conn_rtp);
|
||||
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_SUCCESS));
|
||||
if (silent)
|
||||
goto out_silent;
|
||||
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
|
||||
LOGPCONN(conn, DLMGCP, LOGL_NOTICE,
|
||||
"MDCX: connection successfully modified\n");
|
||||
mgcp_endp_update(endp);
|
||||
return create_response_with_sdp(endp, conn, "MDCX", pdata->trans, false, false);
|
||||
return create_response_with_sdp(endp, conn_rtp, "MDCX", pdata->trans, false, false);
|
||||
error3:
|
||||
return create_err_response(endp, endp, error_code, "MDCX", pdata->trans);
|
||||
|
||||
out_silent:
|
||||
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "MDCX: silent exit\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* DLCX command handler, processes the received command */
|
||||
@@ -1409,11 +1373,10 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
|
||||
struct mgcp_endpoint *endp = rq->endp;
|
||||
struct rate_ctr_group *rate_ctrs;
|
||||
int error_code = 400;
|
||||
int silent = 0;
|
||||
char *line;
|
||||
char stats[1048];
|
||||
const char *conn_id = NULL;
|
||||
struct mgcp_conn_rtp *conn = NULL;
|
||||
struct mgcp_conn *conn = NULL;
|
||||
unsigned int i;
|
||||
|
||||
/* NOTE: In this handler we can not take it for granted that the endp
|
||||
@@ -1449,7 +1412,7 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
|
||||
if (rq->wildcarded) {
|
||||
int num_conns = 0;
|
||||
for (i = 0; i < trunk->number_endpoints; i++) {
|
||||
num_conns += llist_count(&trunk->endpoints[i]->conns);
|
||||
num_conns += mgcp_endp_num_conns(trunk->endpoints[i]);
|
||||
mgcp_endp_release(trunk->endpoints[i]);
|
||||
}
|
||||
rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns);
|
||||
@@ -1493,9 +1456,6 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
|
||||
goto error3;
|
||||
}
|
||||
break;
|
||||
case 'Z':
|
||||
silent = strcasecmp("noanswer", line + 3) == 0;
|
||||
break;
|
||||
default:
|
||||
LOGPEPTR(endp, trunk, DLMGCP, LOGL_NOTICE, "DLCX: Unhandled MGCP option: '%c'/%d\n",
|
||||
line[0], line[0]);
|
||||
@@ -1514,7 +1474,7 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
|
||||
* that we drop all connections on that specific endpoint at once.
|
||||
* (See also RFC3435 Section F.7) */
|
||||
if (!conn_id) {
|
||||
int num_conns = llist_count(&endp->conns);
|
||||
int num_conns = mgcp_endp_num_conns(endp);
|
||||
LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
|
||||
"DLCX: missing ci (connectionIdentifier), will remove all connections (%d total) at once\n",
|
||||
num_conns);
|
||||
@@ -1531,39 +1491,33 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
|
||||
}
|
||||
|
||||
/* Find the connection */
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
conn = mgcp_endp_get_conn(endp, conn_id);
|
||||
if (!conn) {
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CONNID));
|
||||
goto error3;
|
||||
}
|
||||
/* save the statistics of the current connection */
|
||||
mgcp_format_stats(stats, sizeof(stats), conn->conn);
|
||||
mgcp_format_stats(stats, sizeof(stats), conn);
|
||||
|
||||
/* delete connection */
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG, "DLCX: deleting conn:%s\n",
|
||||
mgcp_conn_dump(conn->conn));
|
||||
mgcp_conn_free(endp, conn_id);
|
||||
LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "DLCX: deleting conn:%s\n",
|
||||
mgcp_conn_dump(conn));
|
||||
mgcp_conn_free(conn);
|
||||
LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
|
||||
"DLCX: connection successfully deleted\n");
|
||||
|
||||
/* When all connections are closed, the endpoint will be released
|
||||
* in order to be ready to be used by another call. */
|
||||
if (llist_count(&endp->conns) <= 0) {
|
||||
if (mgcp_endp_num_conns(endp) <= 0) {
|
||||
mgcp_endp_release(endp);
|
||||
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: endpoint released\n");
|
||||
}
|
||||
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS));
|
||||
if (silent)
|
||||
goto out_silent;
|
||||
return create_ok_resp_with_param(endp, endp, 250, "DLCX", pdata->trans, stats);
|
||||
|
||||
error3:
|
||||
return create_err_response(endp, endp, error_code, "DLCX", pdata->trans);
|
||||
|
||||
out_silent:
|
||||
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: silent exit\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* RSIP command handler, processes the received command */
|
||||
@@ -1671,7 +1625,7 @@ static void mgcp_keepalive_timer_cb(void *_trunk)
|
||||
llist_for_each_entry(conn, &endp->conns, entry) {
|
||||
if (conn->type == MGCP_CONN_TYPE_RTP &&
|
||||
conn->mode == MGCP_CONN_RECV_ONLY)
|
||||
send_dummy(endp, &conn->u.rtp);
|
||||
send_dummy(endp, mgcp_conn_get_conn_rtp(conn));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
#include <osmocom/mgcp/mgcp_codec.h>
|
||||
#include <osmocom/mgcp/mgcp_sdp.h>
|
||||
#include <osmocom/mgcp/mgcp_protocol.h>
|
||||
#include <osmocom/mgcp_client/fmtp.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
@@ -56,7 +55,7 @@ struct sdp_rtp_map {
|
||||
};
|
||||
struct sdp_fmtp_param {
|
||||
int payload_type;
|
||||
const char *fmtp;
|
||||
struct mgcp_codec_param param;
|
||||
};
|
||||
|
||||
|
||||
@@ -196,7 +195,11 @@ 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));
|
||||
|
||||
@@ -215,13 +218,40 @@ 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 */
|
||||
/* Advance pointer to the beginning of the parameter section and
|
||||
* tokenize string */
|
||||
str_ptr = strstr(str_ptr, " ");
|
||||
if (!str_ptr)
|
||||
goto error;
|
||||
str_ptr++;
|
||||
|
||||
fmtp_param->fmtp = talloc_strdup(ctx, 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++;
|
||||
}
|
||||
|
||||
exit:
|
||||
talloc_free(str);
|
||||
@@ -269,13 +299,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 const char *param_by_pt(int pt, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len)
|
||||
static struct mgcp_codec_param *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].fmtp;
|
||||
return &fmtp_params[i].param;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@@ -296,6 +326,7 @@ 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;
|
||||
@@ -390,8 +421,8 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
|
||||
/* Store parsed codec information */
|
||||
for (i = 0; i < codecs_used; i++) {
|
||||
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);
|
||||
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);
|
||||
if (rc < 0)
|
||||
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "failed to add codec\n");
|
||||
}
|
||||
@@ -405,12 +436,10 @@ 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%s%s%s",
|
||||
LOGPC(DLMGCP, LOGL_NOTICE, "%d=%s",
|
||||
rtp->codecs[i].payload_type,
|
||||
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] ? "'" : "");
|
||||
strlen(rtp->codecs[i].subtype_name) ? rtp->codecs[i].subtype_name : "unknown");
|
||||
LOGPC(DLMGCP, LOGL_NOTICE, " ");
|
||||
}
|
||||
LOGPC(DLMGCP, LOGL_NOTICE, "\n");
|
||||
|
||||
@@ -418,34 +447,35 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
}
|
||||
|
||||
|
||||
/* Add all codecs related lines to SDP payload */
|
||||
static int add_codecs(struct msgb *sdp, const struct mgcp_conn_rtp *conn)
|
||||
/* Add rtpmap string to the sdp payload, but only when the payload type falls
|
||||
* into the dynamic payload type range */
|
||||
static int add_rtpmap(struct msgb *sdp, int payload_type, const char *audio_name)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (payload_type >= 96 && payload_type <= 127) {
|
||||
if (!audio_name)
|
||||
return -EINVAL;
|
||||
rc = msgb_printf(sdp, "a=rtpmap:%d %s\r\n", payload_type, audio_name);
|
||||
if (rc < 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Add audio strings to sdp payload */
|
||||
static int add_audio(struct msgb *sdp, int *payload_types, unsigned int payload_types_len, int local_port)
|
||||
{
|
||||
int rc;
|
||||
unsigned int i;
|
||||
int local_port;
|
||||
struct mgcp_trunk *trunk = conn->conn->endp->trunk;
|
||||
|
||||
if (!conn->end.codecs_assigned)
|
||||
return 0;
|
||||
|
||||
/* Compose 'm=audio 1234 RTP/AVP 112 96 3' line, with
|
||||
* - local RTP port
|
||||
* - a list of all assigned payload type numbers
|
||||
*/
|
||||
|
||||
if (mgcp_conn_rtp_is_osmux(conn))
|
||||
local_port = trunk->cfg->osmux.local_port;
|
||||
else
|
||||
local_port = conn->end.local_port;
|
||||
|
||||
rc = msgb_printf(sdp, "m=audio %d RTP/AVP", local_port);
|
||||
if (rc < 0)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < conn->end.codecs_assigned; i++) {
|
||||
const struct mgcp_rtp_codec *c = &conn->end.codecs[i];
|
||||
rc = msgb_printf(sdp, " %d", c->payload_type);
|
||||
for (i = 0; i < payload_types_len; i++) {
|
||||
rc = msgb_printf(sdp, " %d", payload_types[i]);
|
||||
if (rc < 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -454,45 +484,31 @@ static int add_codecs(struct msgb *sdp, const struct mgcp_conn_rtp *conn)
|
||||
if (rc < 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* Compose 'a=rtpmap:N FOO' lines for codecs in above list that require it.
|
||||
* e.g. GSM-FR is implicitly defined by payload type number 3, so it is enough to list 3 above;
|
||||
* AMR needs a line like 'a=rtpmap:112 AMR/8000/1' in addition to listing 112 above.
|
||||
*/
|
||||
for (i = 0; i < conn->end.codecs_assigned; i++) {
|
||||
const struct mgcp_rtp_codec *c = &conn->end.codecs[i];
|
||||
if (!c->audio_name[0])
|
||||
continue;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Dynamic payload type numbers need explicit rtpmap defining the codec by "subtype name" like "AMR" or
|
||||
* "GSM-HR-08". Others are defined implicitly, like GSM-FR by payload type number 3.
|
||||
*
|
||||
* Also, if the trunk is configured as "no sdp audio-payload send-name", omit all rtpmap lines.
|
||||
*/
|
||||
if (c->payload_type >= 96 && c->payload_type <= 127
|
||||
&& trunk->audio_send_name) {
|
||||
if (msgb_printf(sdp, "a=rtpmap:%d %s\r\n", c->payload_type, c->audio_name) < 0)
|
||||
/* Add fmtp strings to sdp payload */
|
||||
static int add_fmtp(struct msgb *sdp, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len)
|
||||
{
|
||||
unsigned int i;
|
||||
int rc;
|
||||
|
||||
for (i = 0; i < fmtp_params_len; i++) {
|
||||
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;
|
||||
}
|
||||
|
||||
/* Compose 'a=fmtp:N foo=bar' line if fmtp is defined for this codec.
|
||||
* e.g. AMR has fmtp like 'octet-align=1', 'mode-set=0,2,4,7'.
|
||||
*/
|
||||
if (c->fmtp[0]) {
|
||||
if (msgb_printf(sdp, OSMO_SDP_PREFIX_A_FMTP "%d %s\r\n", c->payload_type, c->fmtp) < 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
else if (c->param_present) {
|
||||
/* Legacy */
|
||||
if (msgb_printf(sdp, OSMO_SDP_PREFIX_A_FMTP "%d %s\r\n", c->payload_type,
|
||||
OSMO_SDP_AMR_SET_OCTET_ALIGN(c->param.amr_octet_aligned))
|
||||
< 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (conn->end.packet_duration_ms > 0 && conn->conn->endp->trunk->audio_send_ptime) {
|
||||
rc = msgb_printf(sdp, "a=ptime:%u\r\n",
|
||||
conn->end.packet_duration_ms);
|
||||
rc = msgb_printf(sdp, "\r\n");
|
||||
if (rc < 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -510,7 +526,15 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
||||
const struct mgcp_conn_rtp *conn, struct msgb *sdp,
|
||||
const char *addr)
|
||||
{
|
||||
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;
|
||||
struct sdp_fmtp_param fmtp_params[1];
|
||||
unsigned int fmtp_params_len = 0;
|
||||
bool addr_is_v6;
|
||||
|
||||
OSMO_ASSERT(endp);
|
||||
@@ -518,6 +542,11 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
||||
OSMO_ASSERT(sdp);
|
||||
OSMO_ASSERT(addr);
|
||||
|
||||
codec = conn->end.codec;
|
||||
|
||||
audio_name = codec->audio_name;
|
||||
payload_type = codec->payload_type;
|
||||
|
||||
addr_is_v6 = osmo_ip_str_type(addr) == AF_INET6;
|
||||
|
||||
rc = msgb_printf(sdp,
|
||||
@@ -532,14 +561,43 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
||||
if (rc < 0)
|
||||
goto buffer_too_small;
|
||||
|
||||
/* Add all codecs related SDP lines */
|
||||
rc = add_codecs(sdp, conn);
|
||||
if (rc < 0)
|
||||
goto buffer_too_small;
|
||||
if (payload_type >= 0) {
|
||||
|
||||
payload_types[0] = payload_type;
|
||||
if (mgcp_conn_rtp_is_osmux(conn))
|
||||
local_port = endp->trunk->cfg->osmux.local_port;
|
||||
else
|
||||
local_port = conn->end.local_port;
|
||||
rc = add_audio(sdp, payload_types, 1, local_port);
|
||||
if (rc < 0)
|
||||
goto buffer_too_small;
|
||||
|
||||
if (endp->trunk->audio_send_name) {
|
||||
rc = add_rtpmap(sdp, payload_type, audio_name);
|
||||
if (rc < 0)
|
||||
goto buffer_too_small;
|
||||
}
|
||||
|
||||
if (codec->param_present) {
|
||||
fmtp_param.payload_type = payload_type;
|
||||
fmtp_param.param = codec->param;
|
||||
fmtp_params[0] = fmtp_param;
|
||||
fmtp_params_len = 1;
|
||||
}
|
||||
rc = add_fmtp(sdp, fmtp_params, fmtp_params_len);
|
||||
if (rc < 0)
|
||||
goto buffer_too_small;
|
||||
}
|
||||
if (conn->end.packet_duration_ms > 0 && endp->trunk->audio_send_ptime) {
|
||||
rc = msgb_printf(sdp, "a=ptime:%u\r\n",
|
||||
conn->end.packet_duration_ms);
|
||||
if (rc < 0)
|
||||
goto buffer_too_small;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
buffer_too_small:
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, "SDP message too large for buffer\n");
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, "SDP messagebuffer too small\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ void mgcp_format_stats(char *str, size_t str_len, struct mgcp_conn *conn)
|
||||
* keep this option open: */
|
||||
switch (conn->type) {
|
||||
case MGCP_CONN_TYPE_RTP:
|
||||
mgcp_format_stats_rtp(str, str_len, &conn->u.rtp);
|
||||
mgcp_format_stats_rtp(str, str_len, mgcp_conn_get_conn_rtp(conn));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -254,7 +254,8 @@ static void dump_endpoint(struct vty *vty, struct mgcp_endpoint *endp,
|
||||
* connection types (E1) as soon as
|
||||
* the implementation is available */
|
||||
if (conn->type == MGCP_CONN_TYPE_RTP) {
|
||||
dump_rtp_end(vty, &conn->u.rtp);
|
||||
struct mgcp_conn_rtp *conn_rtp = mgcp_conn_get_conn_rtp(conn);
|
||||
dump_rtp_end(vty, conn_rtp);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1357,10 +1358,11 @@ DEFUN(loop_conn,
|
||||
endp = trunk->endpoints[endp_no];
|
||||
int loop = atoi(argv[2]);
|
||||
llist_for_each_entry(conn, &endp->conns, entry) {
|
||||
if (conn->type == MGCP_CONN_TYPE_RTP)
|
||||
if (conn->type == MGCP_CONN_TYPE_RTP) {
|
||||
/* Handle it like a MDCX, switch on SSRC patching if enabled */
|
||||
mgcp_rtp_end_config(endp, 1, &conn->u.rtp.end);
|
||||
else {
|
||||
struct mgcp_conn_rtp *conn_rtp = mgcp_conn_get_conn_rtp(conn);
|
||||
mgcp_rtp_end_config(endp, 1, &conn_rtp->end);
|
||||
} else {
|
||||
/* FIXME: Introduce support for other connection (E1)
|
||||
* types when implementation is available */
|
||||
vty_out(vty, "%%Can't enable SSRC patching,"
|
||||
@@ -1418,7 +1420,7 @@ DEFUN(tap_rtp,
|
||||
endp = trunk->endpoints[endp_no];
|
||||
|
||||
conn_id = argv[2];
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
conn = mgcp_endp_get_conn_rtp(endp, conn_id);
|
||||
if (!conn) {
|
||||
vty_out(vty, "Conn ID %s is invalid.%s",
|
||||
conn_id, VTY_NEWLINE);
|
||||
|
||||
38
src/libosmo-sdp/Makefile.am
Normal file
38
src/libosmo-sdp/Makefile.am
Normal file
@@ -0,0 +1,38 @@
|
||||
AM_CPPFLAGS = \
|
||||
$(all_includes) \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(top_builddir) \
|
||||
$(NULL)
|
||||
|
||||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(TALLOC_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
SDP_LIBVERSION=1:0:0
|
||||
|
||||
# This is not at all related to the release version, but a range of supported
|
||||
# API versions. Read TODO_RELEASE in the source tree's root!
|
||||
lib_LTLIBRARIES = \
|
||||
libosmo-sdp.la \
|
||||
$(NULL)
|
||||
|
||||
libosmo_sdp_la_SOURCES = \
|
||||
sdp_codec.c \
|
||||
sdp_codec_list.c \
|
||||
sdp_msg.c \
|
||||
sdp_internal.c \
|
||||
fmtp.c \
|
||||
$(NULL)
|
||||
|
||||
libosmo_sdp_la_LDFLAGS = \
|
||||
$(AM_LDFLAGS) \
|
||||
-version-info $(SDP_LIBVERSION) \
|
||||
-no-undefined \
|
||||
-export-symbols-regex '^osmo_' \
|
||||
$(NULL)
|
||||
|
||||
libosmo_sdp_la_LIBADD = \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(NULL)
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* (C) 2023-2015 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* (C) 2023-2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Neels Hofmeyr
|
||||
@@ -22,12 +22,27 @@
|
||||
#include <ctype.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/mgcp_client/fmtp.h>
|
||||
|
||||
static const char *fmtp_next_option(const char *fmtp)
|
||||
#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)
|
||||
{
|
||||
for (; fmtp && *fmtp && *fmtp != ';'; fmtp++);
|
||||
for (; fmtp && isspace(*fmtp); 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;
|
||||
}
|
||||
|
||||
@@ -37,11 +52,14 @@ static const char *fmtp_next_option(const char *fmtp)
|
||||
*
|
||||
* const char *fmtp_vals = "octet-align=1;mode-set=0,2,4,7";
|
||||
*
|
||||
* char res[23];
|
||||
* if (osmo_sdp_fmtp_get_val(res, sizeof(res), fmtp_vals, "mode-set"))
|
||||
* use_modeset(res);
|
||||
* else
|
||||
* 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.
|
||||
@@ -54,7 +72,7 @@ bool osmo_sdp_fmtp_get_val(char *val, size_t val_size, const char *fmtp, const c
|
||||
const char *pos = fmtp;
|
||||
const char *end;
|
||||
int option_name_len = strlen(option_name);
|
||||
for (; pos && *pos; pos = fmtp_next_option(pos)) {
|
||||
for (; pos && *pos; pos = osmo_sdp_fmtp_next(pos)) {
|
||||
if (!osmo_str_startswith(pos, option_name))
|
||||
continue;
|
||||
pos += option_name_len;
|
||||
@@ -67,7 +85,7 @@ bool osmo_sdp_fmtp_get_val(char *val, size_t val_size, const char *fmtp, const c
|
||||
if (!pos || !*pos)
|
||||
return false;
|
||||
|
||||
end = fmtp_next_option(pos);
|
||||
end = osmo_sdp_fmtp_end(pos);
|
||||
OSMO_ASSERT(end);
|
||||
if (val && val_size)
|
||||
osmo_strlcpy(val, pos, OSMO_MIN(val_size, end - pos + 1));
|
||||
@@ -86,17 +104,16 @@ bool osmo_sdp_fmtp_get_val(char *val, size_t val_size, const char *fmtp, const c
|
||||
* \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.
|
||||
*/
|
||||
int osmo_sdp_fmtp_get_int(const char *fmtp, const char *option_name, int default_value)
|
||||
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;
|
||||
int i;
|
||||
if (osmo_str_to_int(&i, val, 10, 0, 1)) {
|
||||
int64_t i;
|
||||
if (osmo_str_to_int64(&i, val, 10, INT64_MIN, INT64_MAX)) {
|
||||
/* error parsing number */
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Invalid number in fmtp parameter '%s': '%s'\n", option_name, val);
|
||||
return default_value;
|
||||
}
|
||||
return i;
|
||||
@@ -116,5 +133,61 @@ int osmo_sdp_fmtp_get_int(const char *fmtp, const char *option_name, int default
|
||||
*/
|
||||
bool osmo_sdp_fmtp_amr_is_octet_aligned(const char *fmtp)
|
||||
{
|
||||
return osmo_sdp_fmtp_get_int(fmtp, OSMO_SDP_NAME_AMR_OCTET_ALIGN, 0) == 1;
|
||||
return osmo_sdp_fmtp_get_int(fmtp, OSMO_SDP_STR_AMR_OCTET_ALIGN, 0) == 1;
|
||||
}
|
||||
|
||||
static void strip_whitespace(char *str)
|
||||
{
|
||||
char *i = str;
|
||||
char *o = str;
|
||||
for (; *i; i++, o++) {
|
||||
while (isspace(*i))
|
||||
i++;
|
||||
*o = *i;
|
||||
if (!*i)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return true when the two AMR type fmtp strings can be considered equivalent.
|
||||
* - Omission of octet-align is equivalent to having octet-align=0 present (0 is the default).
|
||||
* - Omission of 'mode-set' means, match any and all codec modes. So if either a or b have no 'mode-set', it's a match.
|
||||
* If both have 'mode-set' present, they must be identical to match. Do not sort the mode-set string, but strip
|
||||
* whitespace.
|
||||
* - TODO all other parameters are currently completely ignored.
|
||||
*/
|
||||
bool osmo_sdp_fmtp_amr_match(const char *a, const char *b)
|
||||
{
|
||||
char a_modeset[32] = {};
|
||||
char b_modeset[32] = {};
|
||||
bool a_ok;
|
||||
bool b_ok;
|
||||
|
||||
if (!a)
|
||||
a = "";
|
||||
if (!b)
|
||||
b = "";
|
||||
|
||||
/* octet-align=1. Omission means octet-align=0 */
|
||||
if (osmo_sdp_fmtp_amr_is_octet_aligned(a) != osmo_sdp_fmtp_amr_is_octet_aligned(b))
|
||||
return false;
|
||||
|
||||
/* mode-set=0,1,2,3,4,5,6,7 */
|
||||
a_ok = osmo_sdp_fmtp_get_val(a_modeset, sizeof(a_modeset), a, "mode-set");
|
||||
b_ok = osmo_sdp_fmtp_get_val(b_modeset, sizeof(b_modeset), b, "mode-set");
|
||||
if (a_ok && b_ok) {
|
||||
/* Strip whitespace: We don't know what remote SDP peers may throw at us. There could be whitespace
|
||||
* around the separators like 'mode-set=2,3 ; octet-align=1', which may show up here as whitespace in
|
||||
* the value string as "2,3 ", which would mismatch "2,3". */
|
||||
strip_whitespace(a_modeset);
|
||||
strip_whitespace(b_modeset);
|
||||
if (strcmp(a_modeset, b_modeset))
|
||||
return false;
|
||||
}
|
||||
|
||||
/* TODO: treat other AMR traits, see RFC4867 8.1. Maybe generically match all values that are present?
|
||||
* So far we have no need for other values than octet-align and mode-set. */
|
||||
|
||||
/* No mismatch found, it's a match */
|
||||
return true;
|
||||
}
|
||||
251
src/libosmo-sdp/sdp_codec.c
Normal file
251
src/libosmo-sdp/sdp_codec.c
Normal file
@@ -0,0 +1,251 @@
|
||||
/* Codec management in SDP messages. */
|
||||
/*
|
||||
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <osmocom/sdp/fmtp.h>
|
||||
#include <osmocom/sdp/sdp_codec.h>
|
||||
#include <osmocom/sdp/sdp_internal.h>
|
||||
|
||||
struct osmo_sdp_codec *osmo_sdp_codec_alloc(void *ctx)
|
||||
{
|
||||
return talloc_zero(ctx, struct osmo_sdp_codec);
|
||||
}
|
||||
|
||||
int osmo_sdp_codec_set(struct osmo_sdp_codec *c,
|
||||
int8_t payload_type, const char *encoding_name, unsigned int rate, const char *fmtp)
|
||||
{
|
||||
c->rate = rate;
|
||||
osmo_sdp_codec_set_encoding_name(c, encoding_name);
|
||||
osmo_sdp_codec_set_fmtp(c, fmtp);
|
||||
c->payload_type = payload_type;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_sdp_codec_set_encoding_name(struct osmo_sdp_codec *c, const char *encoding_name)
|
||||
{
|
||||
osmo_talloc_replace_string(c, &c->encoding_name, encoding_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_sdp_codec_set_fmtp(struct osmo_sdp_codec *c, const char *fmtp)
|
||||
{
|
||||
osmo_talloc_replace_string(c, &c->fmtp, fmtp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool osmo_sdp_codec_is_set(const struct osmo_sdp_codec *a)
|
||||
{
|
||||
return a && a->encoding_name && a->encoding_name[0];
|
||||
}
|
||||
|
||||
int osmo_sdp_codec_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_codec *codec)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
if (!codec) {
|
||||
OSMO_STRBUF_PRINTF(sb, "NULL");
|
||||
return sb.chars_needed;
|
||||
}
|
||||
if (codec->encoding_name && codec->encoding_name[0])
|
||||
OSMO_STRBUF_PRINTF(sb, "%s", codec->encoding_name);
|
||||
if (codec->rate != 8000)
|
||||
OSMO_STRBUF_PRINTF(sb, "/%u", codec->rate);
|
||||
if (codec->fmtp && codec->fmtp[0])
|
||||
OSMO_STRBUF_PRINTF(sb, ":%s", codec->fmtp);
|
||||
OSMO_STRBUF_PRINTF(sb, "#%d", codec->payload_type);
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
char *osmo_sdp_codec_to_str_c(void *ctx, const struct osmo_sdp_codec *codec)
|
||||
{
|
||||
OSMO_NAME_C_IMPL(ctx, 32, "osmo_sdp_codec_to_str_c-ERROR", osmo_sdp_codec_to_str_buf, codec)
|
||||
}
|
||||
|
||||
/*! Parse a codec string as from osmo_sdp_codec_to_str_buf() back to an osmo_sdp_codec struct.
|
||||
* Write the parsed result to *dst, using ctx as talloc parent.
|
||||
* The input string is like <encoding_name>[:<fmtp-string>][#<payload-type-nr>]
|
||||
* for example:
|
||||
* "FOO:my-fmtp=1;my-other-fmtp=2#42"
|
||||
* Note that ';' are separators only within the fmtp string. This function does not separate those. In above example,
|
||||
* the fmtp string part is "my-fmtp=val;my-other-fmtp=val2" and ends up in dst->ftmp as-is.
|
||||
* Parse at most str_len characters, or the entire string when str_len < 0 or str_len > strlen(str).
|
||||
* Return 0 on success, negative on failure. */
|
||||
int osmo_sdp_codec_from_str(struct osmo_sdp_codec *dst, const char *str, int str_len)
|
||||
{
|
||||
const char *pos = str;
|
||||
const char *str_end = str + (str_len >= 0 ? str_len : strlen(str));
|
||||
const char *p2;
|
||||
|
||||
struct token token_encoding_name = {};
|
||||
struct token token_rate = {};
|
||||
struct token token_fmtp = {};
|
||||
struct token token_payload_type = {};
|
||||
|
||||
struct token *new_t = NULL;
|
||||
/* start with the encoding name */
|
||||
struct token *t = &token_encoding_name;
|
||||
t->start = pos;
|
||||
|
||||
for (; pos < str_end; pos++) {
|
||||
new_t = NULL;
|
||||
switch (*pos) {
|
||||
case '/':
|
||||
new_t = &token_rate;
|
||||
break;
|
||||
case ':':
|
||||
new_t = &token_fmtp;
|
||||
break;
|
||||
case '#':
|
||||
/* count this '#' only if there is no other one following. It might be part of a fmtp. */
|
||||
for (p2 = pos + 1; p2 < str_end; p2++)
|
||||
if (*p2 == '#')
|
||||
break;
|
||||
if (p2 < str_end && *p2 == '#')
|
||||
break;
|
||||
/* This is the last '#' in the string. Count it only when a digit follows. */
|
||||
if (!isdigit(pos[1]))
|
||||
break;
|
||||
new_t = &token_payload_type;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!new_t)
|
||||
continue;
|
||||
/* If we already have a token for a start character, don't start it again. These may be part of a fmtp
|
||||
* string. */
|
||||
if (new_t == t)
|
||||
continue;
|
||||
t->end = pos;
|
||||
t = new_t;
|
||||
t->start = pos + 1;
|
||||
}
|
||||
t->end = pos;
|
||||
|
||||
token_copy(dst, &dst->encoding_name, &token_encoding_name);
|
||||
if (token_rate.start)
|
||||
dst->rate = atoi(token_rate.start);
|
||||
else
|
||||
dst->rate = 8000;
|
||||
token_copy(dst, &dst->fmtp, &token_fmtp);
|
||||
if (token_payload_type.start)
|
||||
dst->payload_type = atoi(token_payload_type.start);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Compare both payload type number and fmtp string 1:1 */
|
||||
const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_exact = {
|
||||
.payload_type = true,
|
||||
.encoding_name = true,
|
||||
.rate = true,
|
||||
.fmtp = OSMO_SDP_CMP_EXACT,
|
||||
};
|
||||
|
||||
/* Ignore payload type number; compare fmtp string by meaning when possible, else 1:1 */
|
||||
const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_equivalent = {
|
||||
.payload_type = false,
|
||||
.encoding_name = true,
|
||||
.rate = true,
|
||||
.fmtp = OSMO_SDP_CMP_EQUIVALENT,
|
||||
};
|
||||
|
||||
/* Compare only encoding name */
|
||||
const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_name = {
|
||||
.payload_type = false,
|
||||
.encoding_name = true,
|
||||
.rate = false,
|
||||
.fmtp = OSMO_SDP_CMP_IGNORE,
|
||||
};
|
||||
|
||||
extern const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_equivalent;
|
||||
static inline int strcmp_safe(const char *a, const char *b)
|
||||
{
|
||||
return strcmp(a ? : "", b ? : "");
|
||||
}
|
||||
|
||||
/*! Compare encoding name, rate and fmtp, returning cmp result: -1 if a < b, 0 if a == b, 1 if a > b.
|
||||
* Compare as defined in 'cmp':
|
||||
* If cmpf->payload_type is false, ignore payload_type numbers.
|
||||
* If cmpf->rate is false, ignore rate.
|
||||
* If cmpf->fmtp is OSMO_SDP_CMP_IGNORE, ignore fmtp strings.
|
||||
* If cmpf->fmtp is OSMO_SDP_CMP_EXACT, use strcmp() to match fmtp 1:1.
|
||||
* If cmpf->fmtp is OSMO_SDP_CMP_EQUIVALENT, use specific fmtp knowledge to match equivalent entries;
|
||||
* - AMR fmtp matching is done by osmo_sdp_fmtp_amr_match().
|
||||
* - for all others, still compare fmtp 1:1.
|
||||
*/
|
||||
int osmo_sdp_codec_cmp(const struct osmo_sdp_codec *a, const struct osmo_sdp_codec *b,
|
||||
const struct osmo_sdp_codec_cmp_flags *cmpf)
|
||||
{
|
||||
int cmp;
|
||||
if (a == b)
|
||||
return 0;
|
||||
if (!a)
|
||||
return -1;
|
||||
if (!b)
|
||||
return 1;
|
||||
|
||||
if (!cmpf)
|
||||
cmpf = &osmo_sdp_codec_cmp_exact;
|
||||
|
||||
if (cmpf->encoding_name) {
|
||||
cmp = strcmp_safe(a->encoding_name, b->encoding_name);
|
||||
if (cmp)
|
||||
return cmp;
|
||||
}
|
||||
|
||||
if (cmpf->rate) {
|
||||
cmp = OSMO_CMP(a->rate, b->rate);
|
||||
if (cmp)
|
||||
return cmp;
|
||||
}
|
||||
|
||||
switch (cmpf->fmtp) {
|
||||
default:
|
||||
case OSMO_SDP_CMP_EXACT:
|
||||
cmp = strcmp_safe(a->fmtp, b->fmtp);
|
||||
break;
|
||||
|
||||
case OSMO_SDP_CMP_EQUIVALENT:
|
||||
/* In case of AMR, allow logical matching; we only need to do that if the strings differ. */
|
||||
cmp = strcmp_safe(a->fmtp, b->fmtp);
|
||||
if (cmp
|
||||
&& !strcmp_safe("AMR", a->encoding_name)
|
||||
&& osmo_sdp_fmtp_amr_match(a->fmtp, b->fmtp))
|
||||
cmp = 0;
|
||||
break;
|
||||
|
||||
case OSMO_SDP_CMP_IGNORE:
|
||||
cmp = 0;
|
||||
break;
|
||||
}
|
||||
if (cmp)
|
||||
return cmp;
|
||||
|
||||
if (cmpf->payload_type)
|
||||
cmp = OSMO_CMP(a->payload_type, b->payload_type);
|
||||
|
||||
return cmp;
|
||||
}
|
||||
367
src/libosmo-sdp/sdp_codec_list.c
Normal file
367
src/libosmo-sdp/sdp_codec_list.c
Normal file
@@ -0,0 +1,367 @@
|
||||
/* Codec management in SDP messages. */
|
||||
/*
|
||||
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <osmocom/sdp/sdp_codec_list.h>
|
||||
|
||||
struct osmo_sdp_codec_list *osmo_sdp_codec_list_alloc(void *ctx)
|
||||
{
|
||||
struct osmo_sdp_codec_list *codec_list = talloc_zero(ctx, struct osmo_sdp_codec_list);
|
||||
INIT_LLIST_HEAD(&codec_list->list);
|
||||
return codec_list;
|
||||
}
|
||||
|
||||
/*! Free all items contained in this list, do not free the list itself (leave an empty list). */
|
||||
void osmo_sdp_codec_list_free_items(struct osmo_sdp_codec_list *codec_list)
|
||||
{
|
||||
struct osmo_sdp_codec *c;
|
||||
while ((c = osmo_sdp_codec_list_first(codec_list))) {
|
||||
osmo_sdp_codec_list_remove_entry(c);
|
||||
talloc_free(c);
|
||||
}
|
||||
}
|
||||
|
||||
struct osmo_sdp_codec *osmo_sdp_codec_list_add_empty(struct osmo_sdp_codec_list *codec_list)
|
||||
{
|
||||
struct osmo_sdp_codec *c = osmo_sdp_codec_alloc(codec_list);
|
||||
llist_add_tail(&c->entry, &codec_list->list);
|
||||
return c;
|
||||
}
|
||||
|
||||
int8_t osmo_sdp_codec_list_get_unused_dyn_pt_nr(const struct osmo_sdp_codec_list *codec_list, int8_t suggest_pt_nr)
|
||||
{
|
||||
bool present[127 - 96 + 1] = {};
|
||||
const struct osmo_sdp_codec *c;
|
||||
bool suggest_pt_nr_exists = false;
|
||||
int i;
|
||||
|
||||
osmo_sdp_codec_list_foreach (c, codec_list) {
|
||||
if (c->payload_type >= 96 && c->payload_type <= 127)
|
||||
present[c->payload_type - 96] = true;
|
||||
if (c->payload_type == suggest_pt_nr)
|
||||
suggest_pt_nr_exists = true;
|
||||
}
|
||||
|
||||
if (!suggest_pt_nr_exists)
|
||||
return suggest_pt_nr;
|
||||
|
||||
/* The desired number is already taken, see which of the dynamic types is not taken yet */
|
||||
for (i = 96; i <= 127; i++) {
|
||||
/* For dynamic allocations, skip these predefined numbers, taken from enum mgcp_codecs:
|
||||
* CODEC_GSMEFR_8000_1 = 110, 3GPP TS 48.103 table 5.4.2.2.1
|
||||
* CODEC_GSMHR_8000_1 = 111, 3GPP TS 48.103 table 5.4.2.2.1
|
||||
* CODEC_AMR_8000_1 = 112, 3GPP TS 48.103 table 5.4.2.2.1
|
||||
* CODEC_AMRWB_16000_1 = 113, 3GPP TS 48.103 table 5.4.2.2.1
|
||||
* CODEC_CLEARMODE = 120, 3GPP TS 48.103 table 5.4.2.2.1
|
||||
*/
|
||||
if (i >= 110 && i <= 113)
|
||||
continue;
|
||||
else if (i == 120)
|
||||
continue;
|
||||
|
||||
if (!present[i - 96])
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*! Allocate a new entry in codec_list and copy codec's values to it.
|
||||
* If once is NULL, unconditionally add a new codec entry.
|
||||
* If once is non-NULL, do not add a new entry when the list already contains a matching entry; for determining a match,
|
||||
* use the once->flags. For example, if once = &osmo_sdp_codec_cmp_equivalent, look up if codec_list has a similar
|
||||
* codec, and add the new entry only if it is not listed.
|
||||
* See osmo_sdp_codec_cmp() and osmo_sdp_fmtp_amr_match() for details.
|
||||
* Return the new entry, or the equivalent entry already present in the list.
|
||||
*/
|
||||
struct osmo_sdp_codec *osmo_sdp_codec_list_add(struct osmo_sdp_codec_list *codec_list,
|
||||
const struct osmo_sdp_codec *codec,
|
||||
const struct osmo_sdp_codec_cmp_flags *once, bool pick_unused_pt_nr)
|
||||
{
|
||||
struct osmo_sdp_codec *new_entry;
|
||||
int8_t payload_type;
|
||||
|
||||
if (once) {
|
||||
struct osmo_sdp_codec *c;
|
||||
osmo_sdp_codec_list_foreach (c, codec_list)
|
||||
if (!osmo_sdp_codec_cmp(codec, c, once))
|
||||
return c;
|
||||
}
|
||||
|
||||
/* Adjust payload_type number? */
|
||||
payload_type = codec->payload_type;
|
||||
if (pick_unused_pt_nr)
|
||||
payload_type = osmo_sdp_codec_list_get_unused_dyn_pt_nr(codec_list, payload_type);
|
||||
|
||||
/* Take provided values, possibly modified payload_type */
|
||||
new_entry = osmo_sdp_codec_list_add_empty(codec_list);
|
||||
osmo_sdp_codec_set(new_entry, payload_type, codec->encoding_name, codec->rate, codec->fmtp);
|
||||
|
||||
return new_entry;
|
||||
}
|
||||
|
||||
/*! Remove and free all entries from the codec_list that match the given codec according to osmo_sdp_codec_cmp(cmpf).
|
||||
* Return the number of entries freed. */
|
||||
int osmo_sdp_codec_list_remove(struct osmo_sdp_codec_list *codec_list, const struct osmo_sdp_codec *codec,
|
||||
const struct osmo_sdp_codec_cmp_flags *cmpf)
|
||||
{
|
||||
struct osmo_sdp_codec *i, *j;
|
||||
int count = 0;
|
||||
osmo_sdp_codec_list_foreach_safe (i, j, codec_list) {
|
||||
if (osmo_sdp_codec_cmp(i, codec, cmpf))
|
||||
continue;
|
||||
osmo_sdp_codec_list_remove_entry(i);
|
||||
talloc_free(i);
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/*! Unlink an osmo_sdp_codec from an osmo_sdp_codec_list, if the codec instance is part of a list. Do not free the
|
||||
* struct osmo_sdp_codec.
|
||||
*/
|
||||
void osmo_sdp_codec_list_remove_entry(struct osmo_sdp_codec *codec)
|
||||
{
|
||||
/* The codec is not part of a list in these cases:
|
||||
* After talloc_zero(), next == NULL.
|
||||
* After llist_del(), next == LLIST_POISON1. */
|
||||
if (codec->entry.next != NULL
|
||||
&& codec->entry.next != (struct llist_head *)LLIST_POISON1)
|
||||
llist_del(&codec->entry);
|
||||
}
|
||||
|
||||
static inline int strcmp_safe(const char *a, const char *b)
|
||||
{
|
||||
return strcmp(a ? : "", b ? : "");
|
||||
}
|
||||
|
||||
/*! Short single-line representation of a list of SDP audio codecs, convenient for logging.
|
||||
* If summarize == true, collapse variants of the same encoding_name (in practice, don't show all of the various AMR
|
||||
* fmtp permutations). If summarize == false, print each and every codec in full.
|
||||
*/
|
||||
int osmo_sdp_codec_list_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_codec_list *codec_list, bool summarize)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
const struct osmo_sdp_codec *codec;
|
||||
bool first;
|
||||
|
||||
if (llist_empty(&codec_list->list)) {
|
||||
OSMO_STRBUF_PRINTF(sb, "(no-codecs)");
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
if (!summarize) {
|
||||
first = true;
|
||||
osmo_sdp_codec_list_foreach (codec, codec_list) {
|
||||
if (!first)
|
||||
OSMO_STRBUF_PRINTF(sb, " ");
|
||||
OSMO_STRBUF_APPEND(sb, osmo_sdp_codec_to_str_buf, codec);
|
||||
first = false;
|
||||
}
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
/* summarize */
|
||||
first = true;
|
||||
osmo_sdp_codec_list_foreach (codec, codec_list) {
|
||||
const struct osmo_sdp_codec *c2;
|
||||
int count = 0;
|
||||
bool various_pt = false;
|
||||
|
||||
/* When this encoding name has been handled before, skip it now. */
|
||||
osmo_sdp_codec_list_foreach (c2, codec_list) {
|
||||
if (c2 == codec)
|
||||
break;
|
||||
if (!strcmp_safe(codec->encoding_name, c2->encoding_name)) {
|
||||
count = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (count)
|
||||
continue;
|
||||
|
||||
/* Not seen this encoding_name before, count total occurences */
|
||||
count = 0;
|
||||
osmo_sdp_codec_list_foreach (c2, codec_list) {
|
||||
if (!strcmp_safe(codec->encoding_name, c2->encoding_name)) {
|
||||
count++;
|
||||
if (codec->payload_type != c2->payload_type)
|
||||
various_pt = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!first)
|
||||
OSMO_STRBUF_PRINTF(sb, " ");
|
||||
if (count > 1)
|
||||
OSMO_STRBUF_PRINTF(sb, "%d*", count);
|
||||
OSMO_STRBUF_PRINTF(sb, "%s", codec->encoding_name);
|
||||
if (!various_pt)
|
||||
OSMO_STRBUF_PRINTF(sb, "#%d", codec->payload_type);
|
||||
first = false;
|
||||
}
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
char *osmo_sdp_codec_list_to_str_c(void *ctx, const struct osmo_sdp_codec_list *codec_list, bool summarize)
|
||||
{
|
||||
OSMO_NAME_C_IMPL(ctx, 128, "osmo_sdp_codec_list_to_str_c-ERROR", osmo_sdp_codec_list_to_str_buf, codec_list, summarize)
|
||||
}
|
||||
|
||||
/*! Return first entry, or NULL if the list is empty. */
|
||||
struct osmo_sdp_codec *osmo_sdp_codec_list_first(const struct osmo_sdp_codec_list *list)
|
||||
{
|
||||
return llist_first_entry_or_null(&list->list, struct osmo_sdp_codec, entry);
|
||||
}
|
||||
|
||||
/*! Move entries matching 'codec' to the front of the list. Matching is done via osmo_sdp_codec_cmp(cmpf).
|
||||
* Return the number of matches that are now at the front of the list.
|
||||
*/
|
||||
int osmo_sdp_codec_list_move_to_first(struct osmo_sdp_codec_list *codec_list, const struct osmo_sdp_codec *codec,
|
||||
const struct osmo_sdp_codec_cmp_flags *cmpf)
|
||||
{
|
||||
struct llist_head *head = &codec_list->list;
|
||||
struct osmo_sdp_codec *i, *j;
|
||||
int matches_found = 0;
|
||||
osmo_sdp_codec_list_foreach_safe (i, j, codec_list) {
|
||||
if (osmo_sdp_codec_cmp(codec, i, cmpf))
|
||||
continue;
|
||||
/* It's a match, move to the head */
|
||||
osmo_sdp_codec_list_remove_entry(i);
|
||||
llist_add(&i->entry, head);
|
||||
matches_found++;
|
||||
/* If more matches show up later, add them *after* the one just moved to the front. */
|
||||
head = &i->entry;
|
||||
}
|
||||
|
||||
return matches_found;
|
||||
}
|
||||
|
||||
/*! Compare two lists of SDP codecs, returning cmp result: -1 if a < b, 0 if a == b, 1 if a > b.
|
||||
* The two lists are compared in order, item by item, using osmo_sdp_codec_cmp(cmpf).
|
||||
*/
|
||||
int osmo_sdp_codec_list_cmp(const struct osmo_sdp_codec_list *a, const struct osmo_sdp_codec_list *b,
|
||||
const struct osmo_sdp_codec_cmp_flags *cmpf)
|
||||
{
|
||||
const struct llist_head *a_start;
|
||||
const struct llist_head *a_pos;
|
||||
const struct llist_head *b_start;
|
||||
const struct llist_head *b_pos;
|
||||
int cmp;
|
||||
|
||||
/* NULL pointer == empty list */
|
||||
if (a && llist_empty(&a->list))
|
||||
a = NULL;
|
||||
if (b && llist_empty(&a->list))
|
||||
b = NULL;
|
||||
|
||||
/* are one or both empty? */
|
||||
if (a == b)
|
||||
return 0;
|
||||
if (!a)
|
||||
return -1;
|
||||
if (!b)
|
||||
return 1;
|
||||
|
||||
/* compare item by item */
|
||||
a_start = &a->list;
|
||||
a_pos = a_start->next;
|
||||
b_start = &b->list;
|
||||
b_pos = b_start->next;
|
||||
|
||||
for (; a_pos != a_start; a_pos = a_pos->next, b_pos = b_pos->next) {
|
||||
const struct osmo_sdp_codec *codec_a;
|
||||
const struct osmo_sdp_codec *codec_b;
|
||||
|
||||
if (b_pos == b_start) {
|
||||
/* there is an entry in a, but b has already ended. mismatch. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
codec_a = llist_entry(a_pos, struct osmo_sdp_codec, entry);
|
||||
codec_b = llist_entry(b_pos, struct osmo_sdp_codec, entry);
|
||||
cmp = osmo_sdp_codec_cmp(codec_a, codec_b, cmpf);
|
||||
if (cmp)
|
||||
return cmp;
|
||||
}
|
||||
|
||||
if (b_pos != b_start) {
|
||||
/* 'a' has ended, but 'b' has more items. mismatch. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* full match. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Leave only those codecs in 'dst' that are also present in 'other'.
|
||||
* The matching is made by osmo_sdp_codec_cmp(cmpf).
|
||||
* If translate_payload_type_numbers has an effect if 'dst' and 'other' have mismatching payload_type numbers for the
|
||||
* same SDP codec descriptions. If translate_payload_type_numbers is true, take the payload_type numbers from 'other'.
|
||||
* If false, keep payload_type numbers in 'dst' unchanged. */
|
||||
void osmo_sdp_codec_list_intersection(struct osmo_sdp_codec_list *dst, const struct osmo_sdp_codec_list *other,
|
||||
const struct osmo_sdp_codec_cmp_flags *cmpf,
|
||||
bool translate_payload_type_numbers)
|
||||
{
|
||||
struct osmo_sdp_codec *i, *j;
|
||||
osmo_sdp_codec_list_foreach_safe (i, j, dst) {
|
||||
struct osmo_sdp_codec *o;
|
||||
struct osmo_sdp_codec *match = NULL;
|
||||
osmo_sdp_codec_list_foreach (o, other) {
|
||||
if (osmo_sdp_codec_cmp(i, o, cmpf))
|
||||
continue;
|
||||
match = o;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!match) {
|
||||
osmo_sdp_codec_list_remove_entry(i);
|
||||
talloc_free(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (translate_payload_type_numbers)
|
||||
i->payload_type = match->payload_type;
|
||||
}
|
||||
}
|
||||
|
||||
/* Find an entry for the given payload_type number in the given list of codecs. */
|
||||
struct osmo_sdp_codec *osmo_sdp_codec_list_by_payload_type(struct osmo_sdp_codec_list *codec_list, int8_t payload_type)
|
||||
{
|
||||
struct osmo_sdp_codec *codec;
|
||||
osmo_sdp_codec_list_foreach(codec, codec_list) {
|
||||
if (codec->payload_type == payload_type)
|
||||
return codec;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool osmo_sdp_codec_list_is_empty(const struct osmo_sdp_codec_list *codec_list)
|
||||
{
|
||||
if (!codec_list)
|
||||
return true;
|
||||
return llist_empty(&codec_list->list);
|
||||
}
|
||||
121
src/libosmo-sdp/sdp_internal.c
Normal file
121
src/libosmo-sdp/sdp_internal.c
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <osmocom/sdp/sdp_internal.h>
|
||||
|
||||
/* Copy a string from t->start to t->end, return as talloc allocated under ctx in *dst.
|
||||
* If *dst is non-NULL, talloc_free(*dst) first. */
|
||||
void token_copy(void *ctx, char **dst, const struct token *t)
|
||||
{
|
||||
size_t len;
|
||||
if (*dst)
|
||||
talloc_free(*dst);
|
||||
if (!t->start || !(t->end > t->start)) {
|
||||
*dst = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
len = t->end - t->start;
|
||||
*dst = talloc_size(ctx, len + 1);
|
||||
osmo_strlcpy(*dst, t->start, len + 1);
|
||||
talloc_set_name_const(*dst, *dst);
|
||||
}
|
||||
|
||||
const char *token_chr(const struct token *src, char c)
|
||||
{
|
||||
const char *pos;
|
||||
for (pos = src->start; *pos && pos < src->end; pos++) {
|
||||
if (*pos == c)
|
||||
return pos;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *token_chrs(const struct token *src, const char *chrs)
|
||||
{
|
||||
const char *pos;
|
||||
for (pos = src->start; pos && *pos && pos < src->end; pos++) {
|
||||
if (strchr(chrs, *pos))
|
||||
return pos;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void token_next(struct token *t, const char *str, const char *end, const char *separators)
|
||||
{
|
||||
t->start = str;
|
||||
while (strchr(separators, *t->start) && t->start < end)
|
||||
t->start++;
|
||||
t->end = t->start;
|
||||
while (!strchr(separators, *t->end) && t->end < end)
|
||||
t->end++;
|
||||
}
|
||||
|
||||
/* Convert the token to an integer, and return the character afer the integer string that was parsed.
|
||||
* The difference to osmo_str_to_int64() is that this guarantees to only access the memory from t->start to t->end. */
|
||||
const char *token_to_int64(int64_t *result, const struct token *t, int base, int min_val, int max_val)
|
||||
{
|
||||
/* copy the number section over to a temporary buffer, because t->end may terminate the string anywhere, and
|
||||
* functions like strtoll (which osmo_str_to_int64() uses) are not able to stay within a strict buffer length
|
||||
* unless the string is zero terminated at the buffer boundary. */
|
||||
char buf[32];
|
||||
const char *int_end = t->start;
|
||||
if (int_end < t->end && *int_end == '-')
|
||||
int_end++;
|
||||
while (int_end < t->end && isdigit(*int_end))
|
||||
int_end++;
|
||||
if (int_end - t->start >= sizeof(buf))
|
||||
return NULL;
|
||||
osmo_strlcpy(buf, t->start, int_end - t->start + 1);
|
||||
if (osmo_str_to_int64(result, buf, base, min_val, max_val) != 0)
|
||||
return NULL;
|
||||
return int_end;
|
||||
}
|
||||
|
||||
/* Convenience: like token_to_int64() but with a plain int. */
|
||||
const char *token_to_int(int *result, const struct token *t, int base, int min_val, int max_val)
|
||||
{
|
||||
int64_t val;
|
||||
const char *rc = token_to_int64(&val, t, base, min_val, max_val);
|
||||
if (val < INT_MIN) {
|
||||
if (result)
|
||||
*result = INT_MIN;
|
||||
return NULL;
|
||||
}
|
||||
if (val > INT_MAX) {
|
||||
if (result)
|
||||
*result = INT_MAX;
|
||||
return NULL;
|
||||
}
|
||||
if (result)
|
||||
*result = (int)val;
|
||||
return rc;
|
||||
}
|
||||
517
src/libosmo-sdp/sdp_msg.c
Normal file
517
src/libosmo-sdp/sdp_msg.c
Normal file
@@ -0,0 +1,517 @@
|
||||
/* Implementation for SDP message encoding and decoding */
|
||||
/*
|
||||
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <osmocom/sdp/sdp_msg.h>
|
||||
#include <osmocom/sdp/sdp_strings.h>
|
||||
#include <osmocom/sdp/sdp_internal.h>
|
||||
|
||||
static const char * const mdir_str[] = {
|
||||
[OSMO_SDP_MDIR_UNSET] = "-",
|
||||
[OSMO_SDP_MDIR_SENDONLY] = OSMO_SDP_STR_SENDONLY,
|
||||
[OSMO_SDP_MDIR_RECVONLY] = OSMO_SDP_STR_RECVONLY,
|
||||
[OSMO_SDP_MDIR_SENDRECV] = OSMO_SDP_STR_SENDRECV,
|
||||
[OSMO_SDP_MDIR_INACTIVE] = OSMO_SDP_STR_INACTIVE,
|
||||
};
|
||||
|
||||
/*! Convert struct osmo_sdp_msg to the actual SDP protocol representation. */
|
||||
int osmo_sdp_msg_encode_buf(char *dst, size_t dst_size, const struct osmo_sdp_msg *sdp)
|
||||
{
|
||||
const struct osmo_sdp_codec *codec;
|
||||
struct osmo_strbuf sb = { .buf = dst, .len = dst_size };
|
||||
const char *oip;
|
||||
char oipv;
|
||||
const char *ip;
|
||||
char ipv;
|
||||
|
||||
if (!sdp) {
|
||||
OSMO_STRBUF_PRINTF(sb, "%s", "");
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
oip = sdp->origin.addr.ip[0] ? sdp->origin.addr.ip : "0.0.0.0";
|
||||
oipv = (osmo_ip_str_type(oip) == AF_INET6) ? '6' : '4';
|
||||
|
||||
ip = sdp->rtp.ip[0] ? sdp->rtp.ip : "0.0.0.0";
|
||||
ipv = (osmo_ip_str_type(oip) == AF_INET6) ? '6' : '4';
|
||||
|
||||
OSMO_STRBUF_PRINTF(sb,
|
||||
"v=0\r\n"
|
||||
"o=%s %s %s IN IP%c %s\r\n"
|
||||
"s=%s\r\n"
|
||||
"c=IN IP%c %s\r\n"
|
||||
"t=%"PRId64" %"PRId64"\r\n"
|
||||
"m=audio %d RTP/AVP",
|
||||
sdp->origin.username ? : "libosmo-sdp",
|
||||
sdp->origin.sess_id ? : "0", sdp->origin.sess_version ? : "0",
|
||||
oipv, oip,
|
||||
sdp->session_name ? : "-",
|
||||
ipv, ip,
|
||||
sdp->time_active.start,
|
||||
sdp->time_active.stop,
|
||||
sdp->rtp.port);
|
||||
|
||||
/* Append all payload type numbers to 'm=audio <port> RTP/AVP 3 4 112' line */
|
||||
osmo_sdp_codec_list_foreach(codec, sdp->codecs)
|
||||
OSMO_STRBUF_PRINTF(sb, " %d", codec->payload_type);
|
||||
OSMO_STRBUF_PRINTF(sb, "\r\n");
|
||||
|
||||
/* Add details for all codecs */
|
||||
osmo_sdp_codec_list_foreach(codec, sdp->codecs) {
|
||||
if (!osmo_sdp_codec_is_set(codec))
|
||||
continue;
|
||||
OSMO_STRBUF_PRINTF(sb, OSMO_SDP_A_PREFIX(OSMO_SDP_STR_RTPMAP) "%d %s/%d\r\n", codec->payload_type, codec->encoding_name,
|
||||
codec->rate > 0 ? codec->rate : 8000);
|
||||
if (codec->fmtp && codec->fmtp[0])
|
||||
OSMO_STRBUF_PRINTF(sb, OSMO_SDP_A_PREFIX(OSMO_SDP_STR_FMTP) "%d %s\r\n", codec->payload_type, codec->fmtp);
|
||||
}
|
||||
|
||||
if (sdp->ptime)
|
||||
OSMO_STRBUF_PRINTF(sb, OSMO_SDP_A_PREFIX(OSMO_SDP_STR_PTIME) "%d\r\n", sdp->ptime);
|
||||
|
||||
if (sdp->media_direction != OSMO_SDP_MDIR_UNSET && sdp->media_direction < ARRAY_SIZE(mdir_str))
|
||||
OSMO_STRBUF_PRINTF(sb, "a=%s\r\n", mdir_str[sdp->media_direction]);
|
||||
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
char *osmo_sdp_msg_encode_c(void *ctx, const struct osmo_sdp_msg *sdp)
|
||||
{
|
||||
OSMO_NAME_C_IMPL(ctx, 256, "osmo_sdp_msg_to_str_c-ERROR", osmo_sdp_msg_encode_buf, sdp)
|
||||
}
|
||||
|
||||
/* Return the first line ending (or the end of the string) at or after the given string position. */
|
||||
static const char *get_line_end(const struct token *src)
|
||||
{
|
||||
const char *line_end = token_chrs(src, "\r\n");
|
||||
if (!line_end)
|
||||
return src->end;
|
||||
return line_end;
|
||||
}
|
||||
|
||||
/* See if str starts with an attribute like "rtpmap:" or "sendrecv\r\n".
|
||||
* For example:
|
||||
* token_is_attrib({"foo: bar"}, "foo", ':') --> " bar"
|
||||
* token_is_attrib({"sendrev\n"}, "sendrecv", 0) --> "\n"
|
||||
* token_is_attrib({"sendrev\n"}, "foo", 0) --> NULL
|
||||
* On mismatch, return NULL.
|
||||
* On match, return the string after the expect_next_char.
|
||||
* For expect_next_char == 0, match both the end of the string as well as a line ending character ('\r' or '\n'), and in
|
||||
* both cases return the string directly after attrib_name.
|
||||
*/
|
||||
static const char *token_is_attrib(const struct token *str, const char *attrib_name, char expect_next_char)
|
||||
{
|
||||
const char *next_c;
|
||||
int attrib_name_len = strlen(attrib_name);
|
||||
if (str->start + attrib_name_len > str->end)
|
||||
return NULL;
|
||||
if (!osmo_str_startswith(str->start, attrib_name))
|
||||
return NULL;
|
||||
next_c = str->start + strlen(attrib_name);
|
||||
if (next_c > str->end)
|
||||
return NULL;
|
||||
if (next_c == str->end) {
|
||||
if (!expect_next_char)
|
||||
return str->end;
|
||||
return NULL;
|
||||
}
|
||||
if (expect_next_char == *next_c)
|
||||
return next_c + 1;
|
||||
/* Treat expect_next_char == \0 as equivalent with line end */
|
||||
if (!expect_next_char && strchr("\r\n", *next_c))
|
||||
return next_c;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static enum osmo_sdp_media_direcion_e check_for_media_direction(const struct token *str)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(mdir_str); i++) {
|
||||
if (i == OSMO_SDP_MDIR_UNSET)
|
||||
continue;
|
||||
if (token_is_attrib(str, mdir_str[i], 0))
|
||||
return i;
|
||||
}
|
||||
return OSMO_SDP_MDIR_UNSET;
|
||||
}
|
||||
|
||||
static struct osmo_sdp_codec *find_or_create_payload_type(struct osmo_sdp_msg *sdp, unsigned int payload_type)
|
||||
{
|
||||
struct osmo_sdp_codec *codec;
|
||||
codec = osmo_sdp_codec_list_by_payload_type(sdp->codecs, payload_type);
|
||||
if (!codec) {
|
||||
codec = osmo_sdp_codec_list_add_empty(sdp->codecs);
|
||||
codec->payload_type = payload_type;
|
||||
codec->rate = 8000;
|
||||
}
|
||||
return codec;
|
||||
}
|
||||
|
||||
|
||||
static void next_token(struct token *t, const char *str, const char *end)
|
||||
{
|
||||
token_next(t, str, end, " \t");
|
||||
}
|
||||
|
||||
/* parse a line like 'a=rtpmap:0 PCMU/8000', 'a=fmtp:112 octet-align=1; mode-set=4', 'a=ptime:20'.
|
||||
* The src should point at the character after 'a=', e.g. at the start of 'rtpmap', 'fmtp', 'ptime'
|
||||
*/
|
||||
static int sdp_parse_attrib(struct osmo_sdp_msg *sdp, const struct token *src)
|
||||
{
|
||||
unsigned int payload_type;
|
||||
struct osmo_sdp_codec *codec;
|
||||
enum osmo_sdp_media_direcion_e mdir;
|
||||
const char *line_end = get_line_end(src);
|
||||
const char *next;
|
||||
int nr;
|
||||
struct token t;
|
||||
|
||||
if ((next = token_is_attrib(src, OSMO_SDP_STR_RTPMAP, ':'))) {
|
||||
/* "a=rtpmap:96 AMR/8000" */
|
||||
next_token(&t, next, line_end);
|
||||
next = token_to_int(&nr, &t, 10, 0, 127);
|
||||
if (next != t.end)
|
||||
return -EINVAL;
|
||||
payload_type = nr;
|
||||
|
||||
token_next(&t, next, line_end, " \t/");
|
||||
if (t.start >= t.end)
|
||||
return -EINVAL;
|
||||
next = t.end;
|
||||
|
||||
codec = find_or_create_payload_type(sdp, payload_type);
|
||||
token_copy(codec, &codec->encoding_name, &t);
|
||||
|
||||
token_next(&t, next, line_end, " \t/");
|
||||
if (t.start >= t.end) {
|
||||
/* There should be a "/8000" here. If it is missing, let's not be strict about it. */
|
||||
codec->rate = 8000;
|
||||
} else {
|
||||
next = token_to_int(&nr, &t, 10, 0, INT_MAX);
|
||||
if (next != t.end)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* optional channel number, i.e. another "/1" */
|
||||
token_next(&t, t.end, line_end, " \t/");
|
||||
if (t.end > t.start) {
|
||||
int channels;
|
||||
next = token_to_int(&channels, &t, 10, 0, INT_MAX);
|
||||
if (next != t.end)
|
||||
return -EINVAL;
|
||||
if (channels != 1)
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
|
||||
else if ((next = token_is_attrib(src, OSMO_SDP_STR_FMTP, ':'))) {
|
||||
/* "a=fmtp:112 octet-align=1;mode-set=0,1,2,3" */
|
||||
next_token(&t, next, line_end);
|
||||
next = token_to_int(&nr, &t, 10, 0, 127);
|
||||
if (next != t.end)
|
||||
return -EINVAL;
|
||||
payload_type = nr;
|
||||
|
||||
codec = find_or_create_payload_type(sdp, payload_type);
|
||||
|
||||
/* "octet-align..." token */
|
||||
next_token(&t, next, line_end);
|
||||
if (t.start >= line_end)
|
||||
return -EINVAL;
|
||||
|
||||
t.end = line_end;
|
||||
token_copy(codec, &codec->fmtp, &t);
|
||||
}
|
||||
|
||||
else if ((next = token_is_attrib(src, OSMO_SDP_STR_PTIME, ':'))) {
|
||||
/* "a=ptime:20" */
|
||||
next_token(&t, next, line_end);
|
||||
next = token_to_int(&nr, &t, 10, 1, INT_MAX);
|
||||
if (!next)
|
||||
return -EINVAL;
|
||||
/* RFC8866 6.4: it could also be a 'real' nr, but we don't support that. */
|
||||
if (next < t.end)
|
||||
return -ENOTSUP;
|
||||
sdp->ptime = nr;
|
||||
}
|
||||
|
||||
/* "a=sendrecv" ... */
|
||||
else if ((mdir = check_for_media_direction(src)) != OSMO_SDP_MDIR_UNSET) {
|
||||
sdp->media_direction = mdir;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct value_string fixed_payload_types[] = {
|
||||
{ 0, "PCMU" },
|
||||
{ 3, "GSM" },
|
||||
{ 8, "PCMA" },
|
||||
{ 18, "G729" },
|
||||
{ 110, "GSM-EFR" },
|
||||
{ 111, "GSM-HR-08" },
|
||||
{ 112, "AMR" },
|
||||
{ 113, "AMR-WB" },
|
||||
{}
|
||||
};
|
||||
|
||||
/* Parse a line like 'm=audio 16398 RTP/AVP 0 3 8 96 112', starting after the '=' */
|
||||
static int sdp_parse_media_description(struct osmo_sdp_msg *sdp, const struct token *src)
|
||||
{
|
||||
unsigned int port;
|
||||
struct token t;
|
||||
const char *line_end = get_line_end(src);
|
||||
if (sscanf(src->start, "audio %u RTP/AVP", &port) < 1)
|
||||
return -ENOTSUP;
|
||||
|
||||
if (port > 0xffff)
|
||||
return -EINVAL;
|
||||
|
||||
sdp->rtp.port = port;
|
||||
|
||||
/* skip "audio 12345 RTP/AVP ", i.e. 3 tokens on */
|
||||
next_token(&t, src->start, line_end);
|
||||
next_token(&t, t.end, line_end);
|
||||
next_token(&t, t.end, line_end);
|
||||
|
||||
/* first payload type number */
|
||||
next_token(&t, t.end, line_end);
|
||||
|
||||
/* Parse listing of payload type numbers after "RTP/AVP" */
|
||||
while (t.start < line_end) {
|
||||
unsigned int payload_type;
|
||||
struct osmo_sdp_codec *codec;
|
||||
const char *encoding_name;
|
||||
if (sscanf(t.start, "%u", &payload_type) < 1)
|
||||
return -EINVAL;
|
||||
|
||||
codec = find_or_create_payload_type(sdp, payload_type);
|
||||
|
||||
/* Fill in encoding name for fixed payload types */
|
||||
encoding_name = get_value_string_or_null(fixed_payload_types, codec->payload_type);
|
||||
if (encoding_name)
|
||||
osmo_talloc_replace_string(codec, &codec->encoding_name, encoding_name);
|
||||
|
||||
next_token(&t, t.end, line_end);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* parse a line like 'c=IN IP4 192.168.11.151' starting after the '=' */
|
||||
static int sdp_parse_connection_info(struct osmo_sdp_msg *sdp, const struct token *src)
|
||||
{
|
||||
char ipv[10];
|
||||
char addr_str[INET6_ADDRSTRLEN];
|
||||
if (sscanf(src->start, "IN %s %s", ipv, addr_str) < 2)
|
||||
return -EINVAL;
|
||||
|
||||
if (strcmp(ipv, "IP4") && strcmp(ipv, "IP6"))
|
||||
return -ENOTSUP;
|
||||
|
||||
return osmo_sockaddr_str_from_str(&sdp->rtp, addr_str, sdp->rtp.port);
|
||||
}
|
||||
|
||||
/* parse a line like 'o=jdoe 3724394400 3724394405 IN IP4 198.51.100.1' starting after the '=' */
|
||||
static int sdp_parse_origin(struct osmo_sdp_msg *sdp, const struct token *src)
|
||||
{
|
||||
struct token t;
|
||||
char addr_str[INET6_ADDRSTRLEN + 1] = {};
|
||||
const char *line_end = get_line_end(src);
|
||||
|
||||
next_token(&t, src->start, line_end);
|
||||
token_copy(sdp, &sdp->origin.username, &t);
|
||||
|
||||
next_token(&t, t.end, line_end);
|
||||
token_copy(sdp, &sdp->origin.sess_id, &t);
|
||||
|
||||
next_token(&t, t.end, line_end);
|
||||
token_copy(sdp, &sdp->origin.sess_version, &t);
|
||||
|
||||
next_token(&t, t.end, line_end);
|
||||
if (strncmp("IN", t.start, t.end - t.start))
|
||||
return -ENOTSUP;
|
||||
|
||||
next_token(&t, t.end, line_end);
|
||||
if (strncmp("IP4", t.start, t.end - t.start)
|
||||
&& strncmp("IP6", t.start, t.end - t.start))
|
||||
return -ENOTSUP;
|
||||
|
||||
next_token(&t, t.end, line_end);
|
||||
osmo_strlcpy(addr_str, t.start, OSMO_MIN(sizeof(addr_str), t.end - t.start + 1));
|
||||
return osmo_sockaddr_str_from_str(&sdp->origin.addr, addr_str, 0);
|
||||
}
|
||||
|
||||
static int sdp_parse_session_name(struct osmo_sdp_msg *sdp, const struct token *src)
|
||||
{
|
||||
struct token t = *src;
|
||||
t.end = get_line_end(src);
|
||||
if (sdp->session_name)
|
||||
talloc_free(sdp->session_name);
|
||||
if (t.start >= t.end)
|
||||
sdp->session_name = NULL;
|
||||
else
|
||||
token_copy(sdp, &sdp->session_name, &t);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct osmo_sdp_msg *osmo_sdp_msg_alloc(void *ctx)
|
||||
{
|
||||
struct osmo_sdp_msg *sdp;
|
||||
sdp = talloc_zero(ctx, struct osmo_sdp_msg);
|
||||
sdp->codecs = osmo_sdp_codec_list_alloc(sdp);
|
||||
return sdp;
|
||||
}
|
||||
|
||||
/* Parse SDP string into struct osmo_sdp_msg. Return 0 on success, negative on error.
|
||||
* Return a new osmo_sdp_msg instance allocated from ctx, or NULL on error.
|
||||
* When NULL is returned and if ret is non-NULL, details of the error are returned in ret->*.
|
||||
*/
|
||||
struct osmo_sdp_msg *osmo_sdp_msg_decode(void *ctx, const char *src_str, int src_str_len,
|
||||
struct osmo_sdp_msg_decode_ret *ret)
|
||||
{
|
||||
struct osmo_sdp_msg *sdp;
|
||||
bool found_message_start = false;
|
||||
struct token src = {
|
||||
.start = src_str,
|
||||
.end = src_str + (src_str_len < 0 ? strlen(src_str) : src_str_len),
|
||||
};
|
||||
|
||||
if (ret)
|
||||
*ret = (struct osmo_sdp_msg_decode_ret){};
|
||||
|
||||
sdp = osmo_sdp_msg_alloc(ctx);
|
||||
|
||||
for (; src.start && src.start < src.end && *src.start; src.start++) {
|
||||
char attrib;
|
||||
int rc = 0;
|
||||
struct token t;
|
||||
|
||||
if (*src.start == '\r' || *src.start == '\n')
|
||||
continue;
|
||||
|
||||
t.start = src.start;
|
||||
t.end = get_line_end(&src);
|
||||
|
||||
if (!found_message_start) {
|
||||
/* An SDP message must start with a line saying "v=0". */
|
||||
if (strncmp("v=0", t.start, t.end - t.start)) {
|
||||
/* report the error */
|
||||
if (ret) {
|
||||
*ret = (struct osmo_sdp_msg_decode_ret){
|
||||
.rc = -EINVAL,
|
||||
.error = {
|
||||
.at_input_str = t.start,
|
||||
.at_input_str_len = t.end - t.start,
|
||||
},
|
||||
.src_remain = t.end,
|
||||
};
|
||||
}
|
||||
talloc_free(sdp);
|
||||
return NULL;
|
||||
}
|
||||
found_message_start = true;
|
||||
goto next_line;
|
||||
}
|
||||
|
||||
/* Expecting only lines starting with 'X='. Not being too strict about it is probably alright. */
|
||||
if (t.start + 1 >= t.end || t.start[1] != '=')
|
||||
goto next_line;
|
||||
|
||||
attrib = *t.start;
|
||||
t.start += 2;
|
||||
switch (attrib) {
|
||||
/* a=... */
|
||||
case 'a':
|
||||
rc = sdp_parse_attrib(sdp, &t);
|
||||
break;
|
||||
case 'm':
|
||||
rc = sdp_parse_media_description(sdp, &t);
|
||||
break;
|
||||
case 'c':
|
||||
rc = sdp_parse_connection_info(sdp, &t);
|
||||
break;
|
||||
case 'o':
|
||||
rc = sdp_parse_origin(sdp, &t);
|
||||
break;
|
||||
case 's':
|
||||
rc = sdp_parse_session_name(sdp, &t);
|
||||
break;
|
||||
default:
|
||||
/* ignore any other parameters */
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc) {
|
||||
if (ret) {
|
||||
/* shift back to include the 'x=' part as well */
|
||||
t.start -= 2;
|
||||
*ret = (struct osmo_sdp_msg_decode_ret){
|
||||
.rc = rc,
|
||||
.error = {
|
||||
.at_input_str = t.start,
|
||||
.at_input_str_len = t.end - t.start,
|
||||
},
|
||||
.src_remain = t.end,
|
||||
};
|
||||
}
|
||||
talloc_free(sdp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
next_line:
|
||||
src.start = t.end;
|
||||
}
|
||||
|
||||
return sdp;
|
||||
}
|
||||
|
||||
/*! Short single-line representation of an SDP message, convenient for logging.
|
||||
* To obtain a valid SDP message, use osmo_sdp_msg_encode_buf() instead.
|
||||
*/
|
||||
int osmo_sdp_msg_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_msg *sdp, bool summarize)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
if (!sdp) {
|
||||
OSMO_STRBUF_PRINTF(sb, "NULL");
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
OSMO_STRBUF_PRINTF(sb, OSMO_SOCKADDR_STR_FMT, OSMO_SOCKADDR_STR_FMT_ARGS(&sdp->rtp));
|
||||
OSMO_STRBUF_PRINTF(sb, "{");
|
||||
OSMO_STRBUF_APPEND(sb, osmo_sdp_codec_list_to_str_buf, sdp->codecs, summarize);
|
||||
OSMO_STRBUF_PRINTF(sb, "}");
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
char *osmo_sdp_msg_to_str_c(void *ctx, const struct osmo_sdp_msg *sdp, bool summarize)
|
||||
{
|
||||
OSMO_NAME_C_IMPL(ctx, 128, "sdp_msg_to_str_c-ERROR", osmo_sdp_msg_to_str_buf, sdp, summarize)
|
||||
}
|
||||
@@ -25,12 +25,6 @@ osmo_mgw_SOURCES = \
|
||||
$(NULL)
|
||||
|
||||
osmo_mgw_LDADD = \
|
||||
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.la \
|
||||
$(LIBOSMOCTRL_LIBS) \
|
||||
$(LIBOSMONETIF_LIBS) \
|
||||
$(LIBOSMOABIS_LIBS) \
|
||||
$(LIBOSMOTRAU_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
@@ -280,6 +280,31 @@ int mgcp_vty_go_parent(struct vty *vty)
|
||||
return vty->node;
|
||||
}
|
||||
|
||||
static void signal_handler(int signum)
|
||||
{
|
||||
fprintf(stdout, "signal %u received\n", signum);
|
||||
|
||||
switch (signum) {
|
||||
case SIGABRT:
|
||||
/* in case of abort, we want to obtain a talloc report and
|
||||
* then run default SIGABRT handler, who will generate coredump
|
||||
* and abort the process. abort() should do this for us after we
|
||||
* return, but program wouldn't exit if an external SIGABRT is
|
||||
* received.
|
||||
*/
|
||||
talloc_report(tall_vty_ctx, stderr);
|
||||
talloc_report_full(tall_mgw_ctx, stderr);
|
||||
signal(SIGABRT, SIG_DFL);
|
||||
raise(SIGABRT);
|
||||
break;
|
||||
case SIGUSR1:
|
||||
talloc_report(tall_vty_ctx, stderr);
|
||||
talloc_report_full(tall_mgw_ctx, stderr);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static struct vty_app_info vty_info = {
|
||||
.name = "OsmoMGW",
|
||||
@@ -328,6 +353,8 @@ int main(int argc, char **argv)
|
||||
|
||||
msgb_talloc_ctx_init(tall_mgw_ctx, 0);
|
||||
|
||||
signal(SIGABRT, &signal_handler);
|
||||
signal(SIGUSR1, &signal_handler);
|
||||
osmo_init_ignore_signals();
|
||||
osmo_init_logging2(tall_mgw_ctx, &log_info);
|
||||
libosmo_abis_init(tall_mgw_ctx);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
SUBDIRS = \
|
||||
mgcp_client \
|
||||
mgcp \
|
||||
sdp \
|
||||
$(NULL)
|
||||
|
||||
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
|
||||
|
||||
@@ -34,7 +34,7 @@ mgcp_test_SOURCES = \
|
||||
$(NULL)
|
||||
|
||||
mgcp_test_LDADD = \
|
||||
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
|
||||
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.la \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
|
||||
@@ -103,7 +103,7 @@ static void test_strline(void)
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 16002 RTP/AVP 97\r\n" \
|
||||
"a=rtpmap:97 GSM-EFR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define MDCX3A_RET \
|
||||
"200 18983215 OK\r\n" \
|
||||
@@ -115,7 +115,7 @@ static void test_strline(void)
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 16002 RTP/AVP 97\r\n" \
|
||||
"a=rtpmap:97 GSM-EFR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define MDCX3_FMTP_RET \
|
||||
"200 18983215 OK\r\n" \
|
||||
@@ -127,40 +127,49 @@ static void test_strline(void)
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 16006 RTP/AVP 97\r\n" \
|
||||
"a=rtpmap:97 GSM-EFR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define MDCX4_ADDR0000 \
|
||||
"MDCX 18983216 1@mgw MGCP 1.0\r\n" \
|
||||
"M: sendrecv\r" \
|
||||
"M: sendrecv\r\n" \
|
||||
"C: 2\r\n" \
|
||||
"I: %s\r\n" \
|
||||
"L: p:20, a:AMR, nt:IN\r\n" \
|
||||
"\n" \
|
||||
"\r\n" \
|
||||
"v=0\r\n" \
|
||||
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
|
||||
"c=IN IP4 0.0.0.0\r\n" \
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 4441 RTP/AVP 99\r\n" \
|
||||
"a=rtpmap:99 AMR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define MDCX4_ADDR0000_RET \
|
||||
"527 18983216 FAIL\r\n"
|
||||
"200 18983216 OK\r\n" \
|
||||
"\r\n" \
|
||||
"v=0\r\n" \
|
||||
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
|
||||
"s=-\r\n" \
|
||||
"c=IN IP4 0.0.0.0\r\n" \
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 16002 RTP/AVP 99\r\n" \
|
||||
"a=rtpmap:99 AMR/8000\r\n" \
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define MDCX4 \
|
||||
"MDCX 18983217 1@mgw MGCP 1.0\r\n" \
|
||||
"M: sendrecv\r" \
|
||||
"M: sendrecv\r\n" \
|
||||
"C: 2\r\n" \
|
||||
"I: %s\r\n" \
|
||||
"L: p:20, a:AMR, nt:IN\r\n" \
|
||||
"\n" \
|
||||
"\r\n" \
|
||||
"v=0\r\n" \
|
||||
"o=- %s 23 IN IP4 5.6.7.8\r\n" \
|
||||
"c=IN IP4 5.6.7.8\r\n" \
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 4441 RTP/AVP 99\r\n" \
|
||||
"a=rtpmap:99 AMR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define MDCX4_RET(Ident) \
|
||||
"200 " Ident " OK\r\n" \
|
||||
@@ -172,7 +181,7 @@ static void test_strline(void)
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 16002 RTP/AVP 99\r\n" \
|
||||
"a=rtpmap:99 AMR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define MDCX4_RO_RET(Ident) \
|
||||
"200 " Ident " OK\r\n" \
|
||||
@@ -184,87 +193,87 @@ static void test_strline(void)
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 16002 RTP/AVP 112\r\n" \
|
||||
"a=rtpmap:112 AMR\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define MDCX4_PT1 \
|
||||
"MDCX 18983218 1@mgw MGCP 1.0\r\n" \
|
||||
"M: SENDRECV\r" \
|
||||
"M: SENDRECV\r\n" \
|
||||
"C: 2\r\n" \
|
||||
"I: %s\r\n" \
|
||||
"L: p:20-40, a:AMR, nt:IN\r\n" \
|
||||
"\n" \
|
||||
"\r\n" \
|
||||
"v=0\r\n" \
|
||||
"o=- %s 23 IN IP4 5.6.7.8\r\n" \
|
||||
"c=IN IP4 5.6.7.8\r\n" \
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 4441 RTP/AVP 99\r\n" \
|
||||
"a=rtpmap:99 AMR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define MDCX4_PT2 \
|
||||
"MDCX 18983219 1@mgw MGCP 1.0\r\n" \
|
||||
"M: sendrecv\r" \
|
||||
"M: sendrecv\r\n" \
|
||||
"C: 2\r\n" \
|
||||
"I: %s\r\n" \
|
||||
"L: p:20-20, a:AMR, nt:IN\r\n" \
|
||||
"\n" \
|
||||
"\r\n" \
|
||||
"v=0\r\n" \
|
||||
"o=- %s 23 IN IP4 5.6.7.8\r\n" \
|
||||
"c=IN IP4 5.6.7.8\r\n" \
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 4441 RTP/AVP 99\r\n" \
|
||||
"a=rtpmap:99 AMR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define MDCX4_PT3 \
|
||||
"MDCX 18983220 1@mgw MGCP 1.0\r\n" \
|
||||
"M: sendrecv\r" \
|
||||
"M: sendrecv\r\n" \
|
||||
"C: 2\r\n" \
|
||||
"I: %s\r\n" \
|
||||
"L: a:AMR, nt:IN\r\n" \
|
||||
"\n" \
|
||||
"\r\n" \
|
||||
"v=0\r\n" \
|
||||
"o=- %s 23 IN IP4 5.6.7.8\r\n" \
|
||||
"c=IN IP4 5.6.7.8\r\n" \
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 4441 RTP/AVP 99\r\n" \
|
||||
"a=rtpmap:99 AMR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
/* Test different upper/lower case in options */
|
||||
#define MDCX4_PT4 \
|
||||
"MDCX 18983221 1@mgw MGCP 1.0\r\n" \
|
||||
"m: sendrecv\r" \
|
||||
"m: sendrecv\r\n" \
|
||||
"c: 2\r\n" \
|
||||
"i: %s\r\n" \
|
||||
"l: A:amr, NT:IN\r\n" \
|
||||
"\n" \
|
||||
"\r\n" \
|
||||
"v=0\r\n" \
|
||||
"o=- %s 23 IN IP4 5.6.7.8\r\n" \
|
||||
"c=IN IP4 5.6.7.8\r\n" \
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 4441 RTP/AVP 99\r\n" \
|
||||
"a=rtpmap:99 AMR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define MDCX4_SO \
|
||||
"MDCX 18983222 1@mgw MGCP 1.0\r\n" \
|
||||
"M: sendonly\r" \
|
||||
"M: sendonly\r\n" \
|
||||
"C: 2\r\n" \
|
||||
"I: %s\r\n" \
|
||||
"L: p:20, a:AMR, nt:IN\r\n" \
|
||||
"\n" \
|
||||
"\r\n" \
|
||||
"v=0\r\n" \
|
||||
"o=- %s 23 IN IP4 5.6.7.8\r\n" \
|
||||
"c=IN IP4 5.6.7.8\r\n" \
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 4441 RTP/AVP 99\r\n" \
|
||||
"a=rtpmap:99 AMR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define MDCX4_RO \
|
||||
"MDCX 18983223 1@mgw MGCP 1.0\r\n" \
|
||||
"M: recvonly\r" \
|
||||
"M: recvonly\r\n" \
|
||||
"C: 2\r\n" \
|
||||
"I: %s\r\n" \
|
||||
"L: p:20, a:AMR, nt:IN\r\n"
|
||||
@@ -297,7 +306,7 @@ static void test_strline(void)
|
||||
"c=IN IP4 123.12.12.123\r\n" \
|
||||
"m=audio 5904 RTP/AVP 97\r\n" \
|
||||
"a=rtpmap:97 GSM-EFR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define CRCX_RET \
|
||||
"200 2 OK\r\n" \
|
||||
@@ -310,7 +319,7 @@ static void test_strline(void)
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 16002 RTP/AVP 97\r\n" \
|
||||
"a=rtpmap:97 GSM-EFR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define CRCX_RET_NO_RTPMAP \
|
||||
"200 2 OK\r\n" \
|
||||
@@ -322,7 +331,7 @@ static void test_strline(void)
|
||||
"c=IN IP4 0.0.0.0\r\n" \
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 16002 RTP/AVP 97\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define CRCX_FMTP_RET \
|
||||
"200 2 OK\r\n" \
|
||||
@@ -335,17 +344,17 @@ static void test_strline(void)
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 16006 RTP/AVP 97\r\n" \
|
||||
"a=rtpmap:97 GSM-EFR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define CRCX_ZYN \
|
||||
"CRCX 2 1@mgw MGCP 1.0\r" \
|
||||
"M: recvonly\r" \
|
||||
"CRCX 2 1@mgw MGCP 1.0\r\n" \
|
||||
"M: recvonly\r\n" \
|
||||
"C: 2\r\n" \
|
||||
"\n" \
|
||||
"v=0\r" \
|
||||
"c=IN IP4 123.12.12.123\r" \
|
||||
"m=audio 5904 RTP/AVP 97\r" \
|
||||
"a=rtpmap:97 GSM-EFR/8000\r"
|
||||
"\r\n" \
|
||||
"v=0\r\n" \
|
||||
"c=IN IP4 123.12.12.123\r\n" \
|
||||
"m=audio 5904 RTP/AVP 97\r\n" \
|
||||
"a=rtpmap:97 GSM-EFR/8000\r\n"
|
||||
|
||||
#define CRCX_ZYN_RET \
|
||||
"200 2 OK\r\n" \
|
||||
@@ -371,7 +380,7 @@ static void test_strline(void)
|
||||
"c=IN IP4 123.12.12.123\r\n" \
|
||||
"m=audio 5904 RTP/AVP 97\r\n" \
|
||||
"a=rtpmap:97 GSM-EFR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define CRCX_X_OSMO_IGN_RET \
|
||||
"200 2 OK\r\n" \
|
||||
@@ -384,7 +393,95 @@ 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" \
|
||||
"m: recvonly\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 97\r\n" \
|
||||
"a=rtpmap:97 GSM-EFR/8000\r\n" \
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define CRCX_PORT_0_RET \
|
||||
"200 3 OK\r\n" \
|
||||
"I: %s\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 16014 RTP/AVP 97\r\n" \
|
||||
"a=rtpmap:97 GSM-EFR/8000\r\n" \
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define CRCX_PORT_0_IUFP \
|
||||
"CRCX 4 1@mgw MGCP 1.0\r\n" \
|
||||
"m: recvonly\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 CRCX_PORT_0_IUFP_RET \
|
||||
"200 4 OK\r\n" \
|
||||
"I: %s\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 16016 RTP/AVP 96\r\n" \
|
||||
"a=rtpmap:96 VND.3GPP.IUFP/16000\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" \
|
||||
@@ -441,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" \
|
||||
@@ -456,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" \
|
||||
@@ -471,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" \
|
||||
@@ -486,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" \
|
||||
@@ -550,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 mode-change-capability=2; octet-align=1\r\n" \
|
||||
"a=fmtp:111 octet-align=1\r\n" \
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define CRCX_NO_LCO_NO_SDP_RET \
|
||||
@@ -575,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"
|
||||
|
||||
@@ -623,139 +720,11 @@ static const struct mgcp_test tests[] = {
|
||||
{"MDCX_NULL", MDCX_NULL, MDCX_NULL_RET},
|
||||
{"DLCX_NULL", DLCX_NULL, DLCX_NULL_RET},
|
||||
{"RQNT_NULL", RQNT_NULL, RQNT_NULL_RET},
|
||||
{
|
||||
"CRCX_EXPLICIT_EP",
|
||||
/* CRCX for a new endpoint 8@mgw, not using the '*@mgw' wildcard */
|
||||
"CRCX 101 rtpbridge/8@mgw MGCP 1.0\r\n"
|
||||
"m: recvonly\r\n"
|
||||
"C: 2\r\n"
|
||||
"L: p:20\r\n"
|
||||
"\r\n"
|
||||
"v=0\r\n"
|
||||
"c=IN IP4 1.2.3.4\r\n"
|
||||
"m=audio 1234 RTP/AVP 112\r\n"
|
||||
"a=rtpmap:112 AMR/8000\r\n"
|
||||
"a=ptime:20\r\n",
|
||||
"200 101 OK\r\n"
|
||||
"I: %s\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 16014 RTP/AVP 112\r\n"
|
||||
"a=rtpmap:112 AMR/8000\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
},
|
||||
{
|
||||
"CRCX_TWO_PAYLOADS_1",
|
||||
"CRCX 102 rtpbridge/*@mgw MGCP 1.0\r\n"
|
||||
"m: recvonly\r\n"
|
||||
"C: 2\r\n"
|
||||
"L: p:20\r\n"
|
||||
"\r\n"
|
||||
"v=0\r\n"
|
||||
"c=IN IP4 1.2.3.4\r\n"
|
||||
"m=audio 1234 RTP/AVP 112 3\r\n"
|
||||
"a=rtpmap:112 AMR/8000\r\n"
|
||||
"a=ptime:20\r\n",
|
||||
"200 102 OK\r\n"
|
||||
"Z: rtpbridge/2@mgw\r\n"
|
||||
"I: %s\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 16016 RTP/AVP 112 3\r\n"
|
||||
"a=rtpmap:112 AMR/8000\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
},
|
||||
{
|
||||
"CRCX_TWO_PAYLOADS_2",
|
||||
"CRCX 103 rtpbridge/2@mgw MGCP 1.0\r\n"
|
||||
"m: recvonly\r\n"
|
||||
"C: 2\r\n"
|
||||
"L: p:20\r\n"
|
||||
"\r\n"
|
||||
"v=0\r\n"
|
||||
"c=IN IP4 1.2.3.4\r\n"
|
||||
"m=audio 1234 RTP/AVP 3 112\r\n"
|
||||
"a=rtpmap:112 AMR/8000\r\n"
|
||||
"a=ptime:20\r\n",
|
||||
"200 103 OK\r\n"
|
||||
"I: %s\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 16018 RTP/AVP 3 112\r\n"
|
||||
"a=rtpmap:112 AMR/8000\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
},
|
||||
{
|
||||
"CRCX_THREE_PAYLOADS_1",
|
||||
"CRCX 104 rtpbridge/*@mgw MGCP 1.0\r\n"
|
||||
"m: recvonly\r\n"
|
||||
"C: 4\r\n"
|
||||
"L: p:20\r\n"
|
||||
"\r\n"
|
||||
"v=0\r\n"
|
||||
"c=IN IP4 1.2.3.4\r\n"
|
||||
"m=audio 1234 RTP/AVP 112 3 111\r\n"
|
||||
"a=rtpmap:112 AMR/8000\r\n"
|
||||
"a=fmtp:112 octet-align=1;mode-set=0,2,4,7\r\n"
|
||||
"a=rtpmap:111 GSM-HR-08/8000\r\n"
|
||||
"a=ptime:20\r\n",
|
||||
"200 104 OK\r\n"
|
||||
"Z: rtpbridge/3@mgw\r\n"
|
||||
"I: %s\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 16020 RTP/AVP 112 3 111\r\n"
|
||||
"a=rtpmap:112 AMR/8000\r\n"
|
||||
"a=fmtp:112 octet-align=1;mode-set=0,2,4,7\r\n"
|
||||
"a=rtpmap:111 GSM-HR-08/8000\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
},
|
||||
{
|
||||
"CRCX_THREE_PAYLOADS_2",
|
||||
"CRCX 105 rtpbridge/3@mgw MGCP 1.0\r\n"
|
||||
"m: recvonly\r\n"
|
||||
"C: 4\r\n"
|
||||
"L: p:20\r\n"
|
||||
"\r\n"
|
||||
"v=0\r\n"
|
||||
"c=IN IP4 1.2.3.4\r\n"
|
||||
"m=audio 1234 RTP/AVP 3 112 113\r\n"
|
||||
"a=rtpmap:112 AMR/8000\r\n"
|
||||
"a=fmtp:112 octet-align=1;mode-set=0,2,4,7\r\n"
|
||||
"a=rtpmap:113 AMR/8000\r\n"
|
||||
"a=fmtp:113 octet-align=1;mode-set=0,2,4\r\n"
|
||||
"a=ptime:20\r\n",
|
||||
"200 105 OK\r\n"
|
||||
"I: %s\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 16022 RTP/AVP 3 112 113\r\n"
|
||||
"a=rtpmap:112 AMR/8000\r\n"
|
||||
"a=fmtp:112 octet-align=1;mode-set=0,2,4,7\r\n"
|
||||
"a=rtpmap:113 AMR/8000\r\n"
|
||||
"a=fmtp:113 octet-align=1;mode-set=0,2,4\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
},
|
||||
{"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[] = {
|
||||
@@ -786,12 +755,13 @@ static struct msgb *create_msg(const char *str, const char *conn_id)
|
||||
|
||||
static int dummy_packets = 0;
|
||||
/* override and forward */
|
||||
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
|
||||
const struct sockaddr *dest_addr, socklen_t addrlen)
|
||||
int osmo_iofd_sendto_msgb(struct osmo_io_fd *iofd, struct msgb *msg, int flags, const struct osmo_sockaddr *addr)
|
||||
{
|
||||
uint32_t dest_host =
|
||||
htonl(((struct sockaddr_in *)dest_addr)->sin_addr.s_addr);
|
||||
int dest_port = htons(((struct sockaddr_in *)dest_addr)->sin_port);
|
||||
htonl(((struct sockaddr_in *)addr)->sin_addr.s_addr);
|
||||
int dest_port = htons(((struct sockaddr_in *)addr)->sin_port);
|
||||
const uint8_t *buf = msgb_data(msg);
|
||||
size_t len = msgb_length(msg);
|
||||
|
||||
if (len == sizeof(rtp_dummy_payload)
|
||||
&& memcmp(buf, rtp_dummy_payload, sizeof(rtp_dummy_payload)) == 0) {
|
||||
@@ -805,6 +775,8 @@ ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
|
||||
OSMO_ASSERT(dest_host);
|
||||
OSMO_ASSERT(dest_port);
|
||||
|
||||
msgb_free(msg);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
@@ -956,7 +928,7 @@ static void test_messages(void)
|
||||
struct msgb *msg;
|
||||
|
||||
printf("\n================================================\n");
|
||||
printf("Testing %s() %s\n", __func__, t->name);
|
||||
printf("Testing %s\n", t->name);
|
||||
|
||||
dummy_packets = 0;
|
||||
|
||||
@@ -971,7 +943,6 @@ 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);
|
||||
}
|
||||
|
||||
@@ -994,7 +965,7 @@ static void test_messages(void)
|
||||
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
|
||||
OSMO_ASSERT(endp);
|
||||
|
||||
conn = mgcp_conn_get_rtp(endp, "1");
|
||||
conn = mgcp_endp_get_conn_rtp(endp, "1");
|
||||
if (conn) {
|
||||
OSMO_ASSERT(conn);
|
||||
|
||||
@@ -1061,9 +1032,6 @@ static void test_messages(void)
|
||||
/* Reset them again for next test */
|
||||
conn->end.codec->payload_type = PTYPE_NONE;
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
mgcp_endpoints_release(trunk);
|
||||
@@ -1092,7 +1060,7 @@ static void test_retransmission(void)
|
||||
struct msgb *msg;
|
||||
|
||||
printf("\n================================================\n");
|
||||
printf("Testing %s() %s\n", __func__, t->name);
|
||||
printf("Testing %s\n", t->name);
|
||||
|
||||
inp = create_msg(t->req, last_conn_id);
|
||||
msg = mgcp_handle_message(cfg, inp);
|
||||
@@ -1122,9 +1090,6 @@ static void test_retransmission(void)
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
msgb_free(msg);
|
||||
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
mgcp_endpoints_release(trunk);
|
||||
@@ -1243,7 +1208,9 @@ static void test_packet_loss_calc(void)
|
||||
_conn =
|
||||
mgcp_conn_alloc(NULL, &endp, MGCP_CONN_TYPE_RTP,
|
||||
"test-connection");
|
||||
conn = mgcp_conn_get_rtp(&endp, _conn->id);
|
||||
OSMO_ASSERT(_conn);
|
||||
conn = mgcp_endp_get_conn_rtp(&endp, _conn->id);
|
||||
OSMO_ASSERT(conn);
|
||||
state = &conn->state;
|
||||
packets_rx = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_RX_CTR);
|
||||
|
||||
@@ -1263,7 +1230,7 @@ static void test_packet_loss_calc(void)
|
||||
pl_test_dat[i].expected);
|
||||
}
|
||||
|
||||
mgcp_conn_free_all(&endp);
|
||||
mgcp_endp_free_conn_all(&endp);
|
||||
}
|
||||
|
||||
talloc_free(trunk);
|
||||
@@ -1494,7 +1461,7 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
||||
_conn = mgcp_conn_alloc(NULL, &endp, MGCP_CONN_TYPE_RTP,
|
||||
"test-connection");
|
||||
OSMO_ASSERT(_conn);
|
||||
conn = mgcp_conn_get_rtp(&endp, _conn->id);
|
||||
conn = mgcp_endp_get_conn_rtp(&endp, _conn->id);
|
||||
OSMO_ASSERT(conn);
|
||||
|
||||
rtp = &conn->end;
|
||||
@@ -1546,7 +1513,7 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
||||
}
|
||||
|
||||
force_monotonic_time_us = -1;
|
||||
mgcp_conn_free_all(&endp);
|
||||
mgcp_endp_free_conn_all(&endp);
|
||||
talloc_free(trunk);
|
||||
talloc_free(cfg);
|
||||
}
|
||||
@@ -1581,7 +1548,7 @@ static void test_multilple_codec(void)
|
||||
OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/1@mgw") == 0);
|
||||
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
|
||||
OSMO_ASSERT(endp);
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
conn = mgcp_endp_get_conn_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 18);
|
||||
|
||||
@@ -1597,7 +1564,7 @@ static void test_multilple_codec(void)
|
||||
OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/2@mgw") == 0);
|
||||
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
|
||||
OSMO_ASSERT(endp);
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
conn = mgcp_endp_get_conn_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 18);
|
||||
|
||||
@@ -1618,7 +1585,7 @@ static void test_multilple_codec(void)
|
||||
OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/3@mgw") == 0);
|
||||
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
|
||||
OSMO_ASSERT(endp);
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
conn = mgcp_endp_get_conn_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 0);
|
||||
|
||||
@@ -1634,7 +1601,7 @@ static void test_multilple_codec(void)
|
||||
OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/4@mgw") == 0);
|
||||
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
|
||||
OSMO_ASSERT(endp);
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
conn = mgcp_endp_get_conn_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 18);
|
||||
|
||||
@@ -1650,7 +1617,7 @@ static void test_multilple_codec(void)
|
||||
OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/5@mgw") == 0);
|
||||
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
|
||||
OSMO_ASSERT(endp);
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
conn = mgcp_endp_get_conn_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 0);
|
||||
|
||||
@@ -1662,7 +1629,7 @@ static void test_multilple_codec(void)
|
||||
OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/5@mgw") == 0);
|
||||
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
|
||||
OSMO_ASSERT(endp);
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
conn = mgcp_endp_get_conn_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 3);
|
||||
OSMO_ASSERT(osmo_sockaddr_port(&conn->end.addr.u.sa) == 16434);
|
||||
@@ -1679,7 +1646,7 @@ static void test_multilple_codec(void)
|
||||
talloc_free(endp->last_response);
|
||||
talloc_free(endp->last_trans);
|
||||
endp->last_response = endp->last_trans = NULL;
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
conn = mgcp_endp_get_conn_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(!conn);
|
||||
|
||||
last_endpoint[0] = '\0';
|
||||
@@ -1693,7 +1660,7 @@ static void test_multilple_codec(void)
|
||||
OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/5@mgw") == 0);
|
||||
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
|
||||
OSMO_ASSERT(endp);
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
conn = mgcp_endp_get_conn_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 0);
|
||||
|
||||
@@ -1722,7 +1689,7 @@ static void test_no_cycle(void)
|
||||
_conn = mgcp_conn_alloc(NULL, endp, MGCP_CONN_TYPE_RTP,
|
||||
"test-connection");
|
||||
OSMO_ASSERT(_conn);
|
||||
conn = mgcp_conn_get_rtp(endp, _conn->id);
|
||||
conn = mgcp_endp_get_conn_rtp(endp, _conn->id);
|
||||
OSMO_ASSERT(conn);
|
||||
|
||||
OSMO_ASSERT(conn->state.stats.initialized == 0);
|
||||
@@ -1753,7 +1720,6 @@ static void test_no_cycle(void)
|
||||
talloc_free(cfg);
|
||||
}
|
||||
|
||||
/* Set audio_send_name=0 and verify that a=rtpmap: entries are omitted. */
|
||||
static void test_no_name(void)
|
||||
{
|
||||
struct mgcp_trunk *trunk;
|
||||
@@ -2366,7 +2332,7 @@ void test_conn_id_matching(void)
|
||||
for (i = 0; i < ARRAY_SIZE(conn_id_request); i++) {
|
||||
const char *needle = conn_id_request[i];
|
||||
printf("needle='%s' ", needle);
|
||||
conn_match = mgcp_conn_get(&endp, needle);
|
||||
conn_match = mgcp_endp_get_conn(&endp, needle);
|
||||
OSMO_ASSERT(conn_match);
|
||||
printf("found '%s'\n", conn_match->id);
|
||||
OSMO_ASSERT(conn_match == conn);
|
||||
@@ -2472,13 +2438,6 @@ int main(int argc, char **argv)
|
||||
void *ctx = talloc_named_const(NULL, 0, "mgcp_test");
|
||||
void *msgb_ctx = msgb_talloc_ctx_init(ctx, 0);
|
||||
osmo_init_logging2(ctx, &log_info);
|
||||
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_BASENAME);
|
||||
log_set_print_filename_pos(osmo_stderr_target, LOG_FILENAME_POS_LINE_END);
|
||||
log_set_print_level(osmo_stderr_target, 1);
|
||||
log_set_print_category(osmo_stderr_target, 1);
|
||||
log_set_print_category_hex(osmo_stderr_target, 0);
|
||||
log_set_use_color(osmo_stderr_target, 1);
|
||||
log_set_category_filter(osmo_stderr_target, DLMGCP, true, LOGL_DEBUG);
|
||||
|
||||
test_strline();
|
||||
test_values();
|
||||
|
||||
@@ -13,7 +13,7 @@ line: ''
|
||||
line: ''
|
||||
|
||||
================================================
|
||||
Testing test_messages() AUEP1
|
||||
Testing AUEP1
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
AUEP 158663169 ds/e1-1/2@mgw MGCP 1.0
|
||||
@@ -25,7 +25,7 @@ Response matches our expectations.
|
||||
(response does not contain a connection id)
|
||||
|
||||
================================================
|
||||
Testing test_messages() AUEP2
|
||||
Testing AUEP2
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
AUEP 18983213 ds/e1-2/1@mgw MGCP 1.0
|
||||
@@ -37,7 +37,7 @@ Response matches our expectations.
|
||||
(response does not contain a connection id)
|
||||
|
||||
================================================
|
||||
Testing test_messages() MDCX1
|
||||
Testing MDCX1
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
MDCX 18983213 ds/e1-3/1@mgw MGCP 1.0
|
||||
@@ -49,7 +49,7 @@ Response matches our expectations.
|
||||
(response does not contain a connection id)
|
||||
|
||||
================================================
|
||||
Testing test_messages() MDCX2
|
||||
Testing MDCX2
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
MDCX 18983214 ds/e1-1/2@mgw MGCP 1.0
|
||||
@@ -61,7 +61,7 @@ Response matches our expectations.
|
||||
(response does not contain a connection id)
|
||||
|
||||
================================================
|
||||
Testing test_messages() CRCX
|
||||
Testing CRCX
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
CRCX 2 1@mgw MGCP 1.0
|
||||
@@ -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:
|
||||
@@ -83,7 +83,7 @@ Response matches our expectations.
|
||||
Dummy packets: 2
|
||||
|
||||
================================================
|
||||
Testing test_messages() MDCX3
|
||||
Testing MDCX3
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
MDCX 18983215 1@mgw MGCP 1.0
|
||||
@@ -97,44 +97,46 @@ Response matches our expectations.
|
||||
Dummy packets: 2
|
||||
|
||||
================================================
|
||||
Testing test_messages() MDCX4_ADDR000
|
||||
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:
|
||||
@@ -144,21 +146,22 @@ Response matches our expectations.
|
||||
Dummy packets: 2
|
||||
|
||||
================================================
|
||||
|
||||
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:
|
||||
@@ -168,21 +171,22 @@ Response matches our expectations.
|
||||
Dummy packets: 2
|
||||
|
||||
================================================
|
||||
Dummy packets: 2
|
||||
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:
|
||||
@@ -192,21 +196,22 @@ Response matches our expectations.
|
||||
Dummy packets: 2
|
||||
|
||||
================================================
|
||||
(response contains a connection id)
|
||||
Testing MDCX4_PT3
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
MDCX 18983220 1@mgw MGCP 1.0
|
||||
Testing test_messages() 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:
|
||||
@@ -216,21 +221,22 @@ Response matches our expectations.
|
||||
Dummy packets: 2
|
||||
|
||||
================================================
|
||||
Response matches our expectations.
|
||||
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:
|
||||
@@ -240,21 +246,22 @@ Response matches our expectations.
|
||||
Dummy packets: 2
|
||||
|
||||
================================================
|
||||
using message with patched conn_id for comparison
|
||||
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:
|
||||
@@ -263,11 +270,12 @@ Response matches our expectations.
|
||||
(response contains a connection id)
|
||||
|
||||
================================================
|
||||
---------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
|
||||
|
||||
@@ -279,7 +287,7 @@ Response matches our expectations.
|
||||
Dummy packets: 2
|
||||
|
||||
================================================
|
||||
---------8<---------
|
||||
Testing DLCX
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
DLCX 7 1@mgw MGCP 1.0
|
||||
@@ -293,12 +301,18 @@ Response matches our expectations.
|
||||
(response does not contain a connection id)
|
||||
|
||||
================================================
|
||||
|
||||
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
|
||||
@@ -307,7 +321,7 @@ Response matches our expectations.
|
||||
Dummy packets: 2
|
||||
|
||||
================================================
|
||||
|
||||
Testing EMPTY
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
|
||||
@@ -315,7 +329,7 @@ creating message from statically defined input:
|
||||
---------8<---------
|
||||
|
||||
================================================
|
||||
Response matches our expectations.
|
||||
Testing SHORT1
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
CRCX
|
||||
@@ -327,7 +341,7 @@ Response matches our expectations.
|
||||
(response does not contain a connection id)
|
||||
|
||||
================================================
|
||||
================================================
|
||||
Testing SHORT2
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
CRCX 1
|
||||
@@ -338,7 +352,7 @@ Response matches our expectations.
|
||||
(response does not contain a connection id)
|
||||
|
||||
================================================
|
||||
|
||||
Testing SHORT3
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
CRCX 1 1@mgw
|
||||
@@ -349,7 +363,7 @@ Response matches our expectations.
|
||||
(response does not contain a connection id)
|
||||
|
||||
================================================
|
||||
|
||||
Testing SHORT4
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
CRCX 1 1@mgw MGCP
|
||||
@@ -360,7 +374,7 @@ Response matches our expectations.
|
||||
(response does not contain a connection id)
|
||||
|
||||
================================================
|
||||
|
||||
Testing RQNT1
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
RQNT 186908780 1@mgw MGCP 1.0
|
||||
@@ -374,7 +388,7 @@ Response matches our expectations.
|
||||
(response does not contain a connection id)
|
||||
|
||||
================================================
|
||||
creating message from statically defined input:
|
||||
Testing RQNT2
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
RQNT 186908781 1@mgw MGCP 1.0
|
||||
@@ -388,7 +402,7 @@ Response matches our expectations.
|
||||
(response does not contain a connection id)
|
||||
|
||||
================================================
|
||||
creating message from statically defined input:
|
||||
Testing DLCX
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
DLCX 7 1@mgw MGCP 1.0
|
||||
@@ -402,7 +416,7 @@ Response matches our expectations.
|
||||
(response does not contain a connection id)
|
||||
|
||||
================================================
|
||||
creating message from statically defined input:
|
||||
Testing CRCX
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
CRCX 2 1@mgw MGCP 1.0
|
||||
@@ -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:
|
||||
@@ -424,7 +438,7 @@ Response matches our expectations.
|
||||
Dummy packets: 2
|
||||
|
||||
================================================
|
||||
c=IN IP4 123.12.12.123
|
||||
Testing MDCX3
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
MDCX 18983215 1@mgw MGCP 1.0
|
||||
@@ -438,7 +452,7 @@ Response matches our expectations.
|
||||
Dummy packets: 2
|
||||
|
||||
================================================
|
||||
creating message from statically defined input:
|
||||
Testing DLCX
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
DLCX 7 1@mgw MGCP 1.0
|
||||
@@ -452,7 +466,7 @@ Response matches our expectations.
|
||||
(response does not contain a connection id)
|
||||
|
||||
================================================
|
||||
creating message from statically defined input:
|
||||
Testing CRCX
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
CRCX 2 6@mgw MGCP 1.0
|
||||
@@ -466,7 +480,7 @@ Response matches our expectations.
|
||||
(response contains a connection id)
|
||||
|
||||
================================================
|
||||
creating message from statically defined input:
|
||||
Testing CRCX
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
CRCX 2 1@mgw MGCP 1.0
|
||||
@@ -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 test_messages() CRCX
|
||||
a=ptime:20
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
@@ -489,7 +503,7 @@ Response matches our expectations.
|
||||
Dummy packets: 2
|
||||
|
||||
================================================
|
||||
c=IN IP4 123.12.12.123
|
||||
Testing MDCX_TOO_LONG_CI
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
MDCX 18983224 1@mgw MGCP 1.0
|
||||
@@ -502,7 +516,7 @@ Response matches our expectations.
|
||||
(response does not contain a connection id)
|
||||
|
||||
================================================
|
||||
Testing test_messages() MDCX_TOO_LONG_CI
|
||||
Testing CRCX
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
CRCX 2 7@mgw MGCP 1.0
|
||||
@@ -526,7 +540,7 @@ Response matches our expectations.
|
||||
Dummy packets: 2
|
||||
|
||||
================================================
|
||||
m=audio 5904 RTP/AVP 111
|
||||
Testing AUEP_NULL
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
AUEP 18983215 null@mgw MGCP 1.0
|
||||
@@ -538,7 +552,7 @@ Response matches our expectations.
|
||||
(response does not contain a connection id)
|
||||
|
||||
================================================
|
||||
================================================
|
||||
Testing CRCX_NULL
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
CRCX 2 null@mgw MGCP 1.0
|
||||
@@ -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:
|
||||
@@ -559,7 +573,7 @@ Response matches our expectations.
|
||||
(response does not contain a connection id)
|
||||
|
||||
================================================
|
||||
v=0
|
||||
Testing MDCX_NULL
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
MDCX 9 null@mgw MGCP 1.0
|
||||
@@ -572,7 +586,7 @@ Response matches our expectations.
|
||||
(response does not contain a connection id)
|
||||
|
||||
================================================
|
||||
Testing test_messages() MDCX_NULL
|
||||
Testing DLCX_NULL
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
DLCX 8 null@mgw MGCP 1.0
|
||||
@@ -586,7 +600,7 @@ Response matches our expectations.
|
||||
(response does not contain a connection id)
|
||||
|
||||
================================================
|
||||
creating message from statically defined input:
|
||||
Testing RQNT_NULL
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
RQNT 186908782 null@mgw MGCP 1.0
|
||||
@@ -600,18 +614,18 @@ Response matches our expectations.
|
||||
(response does not contain a connection id)
|
||||
|
||||
================================================
|
||||
creating message from statically defined input:
|
||||
Testing CRCX_PORT_0
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
X: B244F267488
|
||||
CRCX 3 1@mgw MGCP 1.0
|
||||
m: recvonly
|
||||
C: 2
|
||||
L: p:20
|
||||
|
||||
v=0
|
||||
Response matches our expectations.
|
||||
(response does not contain a connection id)
|
||||
|
||||
c=IN IP4 123.12.12.123
|
||||
m=audio 0 RTP/AVP 97
|
||||
a=rtpmap:97 GSM-EFR/8000
|
||||
a=ptime:20
|
||||
|
||||
---------8<---------
|
||||
@@ -619,21 +633,20 @@ checking response:
|
||||
using message with patched conn_id for comparison
|
||||
Response matches our expectations.
|
||||
(response contains a connection id)
|
||||
L: p:20
|
||||
|
||||
================================================
|
||||
c=IN IP4 1.2.3.4
|
||||
Testing CRCX_PORT_0_IUFP
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
a=ptime:20
|
||||
CRCX 4 1@mgw MGCP 1.0
|
||||
m: recvonly
|
||||
C: 2
|
||||
L: p:20
|
||||
|
||||
v=0
|
||||
(response contains a connection id)
|
||||
Dummy packets: 2
|
||||
|
||||
c=IN IP4 123.12.12.123
|
||||
m=audio 0 RTP/AVP 96
|
||||
a=rtpmap:96 VND.3GPP.IUFP/16000
|
||||
a=ptime:20
|
||||
|
||||
---------8<---------
|
||||
@@ -641,21 +654,20 @@ checking response:
|
||||
using message with patched conn_id for comparison
|
||||
Response matches our expectations.
|
||||
(response contains a connection id)
|
||||
L: p:20
|
||||
|
||||
================================================
|
||||
c=IN IP4 1.2.3.4
|
||||
Testing CRCX_PORT_0_IUFP_SENDRECV
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
a=ptime:20
|
||||
|
||||
CRCX 4 1@mgw MGCP 1.0
|
||||
M: sendrecv
|
||||
C: 2
|
||||
L: p:20
|
||||
|
||||
v=0
|
||||
(response contains a connection id)
|
||||
Dummy packets: 2
|
||||
|
||||
c=IN IP4 123.12.12.123
|
||||
m=audio 0 RTP/AVP 96
|
||||
a=rtpmap:96 VND.3GPP.IUFP/16000
|
||||
a=ptime:20
|
||||
|
||||
---------8<---------
|
||||
@@ -663,23 +675,20 @@ checking response:
|
||||
using message with patched conn_id for comparison
|
||||
Response matches our expectations.
|
||||
(response contains a connection id)
|
||||
L: p:20
|
||||
|
||||
================================================
|
||||
c=IN IP4 1.2.3.4
|
||||
Testing CRCX_PORT_0_IUFP_SENDRECV2
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
a=ptime:20
|
||||
|
||||
---------8<---------
|
||||
CRCX 4 1@mgw MGCP 1.0
|
||||
C: 2
|
||||
L: p:20
|
||||
|
||||
v=0
|
||||
(response contains a connection id)
|
||||
Dummy packets: 2
|
||||
|
||||
================================================
|
||||
Testing test_messages() CRCX_THREE_PAYLOADS_1
|
||||
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<---------
|
||||
@@ -687,24 +696,19 @@ checking response:
|
||||
using message with patched conn_id for comparison
|
||||
Response matches our expectations.
|
||||
(response contains a connection id)
|
||||
v=0
|
||||
|
||||
================================================
|
||||
a=rtpmap:112 AMR/8000
|
||||
Testing CRCX_PORT_0_IUFP_SENDRECV3
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
a=ptime:20
|
||||
|
||||
---------8<---------
|
||||
CRCX 4 1@mgw MGCP 1.0
|
||||
C: 2
|
||||
L: p:20
|
||||
|
||||
v=0
|
||||
(response contains a connection id)
|
||||
Dummy packets: 2
|
||||
|
||||
================================================
|
||||
Testing test_messages() CRCX_THREE_PAYLOADS_2
|
||||
creating message from statically defined input:
|
||||
c=IN IP4 123.12.12.123
|
||||
m=audio 0 RTP/AVP 96
|
||||
a=rtpmap:96 VND.3GPP.IUFP/16000
|
||||
a=ptime:20
|
||||
|
||||
---------8<---------
|
||||
@@ -712,10 +716,9 @@ checking response:
|
||||
using message with patched conn_id for comparison
|
||||
Response matches our expectations.
|
||||
(response contains a connection id)
|
||||
c=IN IP4 1.2.3.4
|
||||
|
||||
================================================
|
||||
a=fmtp:112 octet-align=1;mode-set=0,2,4,7
|
||||
Testing CRCX
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
CRCX 2 1@mgw MGCP 1.0
|
||||
@@ -727,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:
|
||||
@@ -745,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:
|
||||
@@ -753,7 +756,7 @@ using message with patched conn_id for comparison
|
||||
Response matches our expectations.
|
||||
|
||||
================================================
|
||||
|
||||
Testing RQNT1
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
RQNT 186908780 1@mgw MGCP 1.0
|
||||
@@ -777,7 +780,7 @@ using message as statically defined for comparison
|
||||
Response matches our expectations.
|
||||
|
||||
================================================
|
||||
Re-transmitting RQNT1
|
||||
Testing RQNT2
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
RQNT 186908781 1@mgw MGCP 1.0
|
||||
@@ -801,7 +804,7 @@ using message as statically defined for comparison
|
||||
Response matches our expectations.
|
||||
|
||||
================================================
|
||||
Re-transmitting RQNT2
|
||||
Testing MDCX3
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
MDCX 18983215 1@mgw MGCP 1.0
|
||||
@@ -823,7 +826,7 @@ using message with patched conn_id for comparison
|
||||
Response matches our expectations.
|
||||
|
||||
================================================
|
||||
Response matches our expectations.
|
||||
Testing DLCX
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
DLCX 7 1@mgw MGCP 1.0
|
||||
@@ -857,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:
|
||||
@@ -1311,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:
|
||||
@@ -1328,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:
|
||||
@@ -1345,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:
|
||||
@@ -1362,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:
|
||||
@@ -1444,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:
|
||||
|
||||
@@ -571,6 +571,57 @@ static void test_map_str_to_codec(void)
|
||||
OSMO_ASSERT(map_str_to_codec("AMR-WB####################################################################################################################") == -1);
|
||||
}
|
||||
|
||||
static void test_map_codec_to_pt_and_map_pt_to_codec(void)
|
||||
{
|
||||
struct ptmap ptmap[10];
|
||||
unsigned int ptmap_len;
|
||||
unsigned int i;
|
||||
|
||||
ptmap[0].codec = CODEC_GSMEFR_8000_1;
|
||||
ptmap[0].pt = 96;
|
||||
ptmap[1].codec = CODEC_GSMHR_8000_1;
|
||||
ptmap[1].pt = 97;
|
||||
ptmap[2].codec = CODEC_AMR_8000_1;
|
||||
ptmap[2].pt = 98;
|
||||
ptmap[3].codec = CODEC_AMRWB_16000_1;
|
||||
ptmap[3].pt = 99;
|
||||
ptmap_len = 4;
|
||||
|
||||
/* Mappings that are covered by the table */
|
||||
for (i = 0; i < ptmap_len; i++)
|
||||
printf(" %u => %u\n", ptmap[i].codec, map_codec_to_pt(ptmap, ptmap_len, ptmap[i].codec));
|
||||
for (i = 0; i < ptmap_len; i++)
|
||||
printf(" %u <= %u\n", ptmap[i].pt, map_pt_to_codec(ptmap, ptmap_len, ptmap[i].pt));
|
||||
printf("\n");
|
||||
|
||||
/* Map some codecs/payload types from the static range, result must
|
||||
* always be a 1:1 mapping */
|
||||
printf(" %u => %u\n", CODEC_PCMU_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_PCMU_8000_1));
|
||||
printf(" %u => %u\n", CODEC_GSM_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_GSM_8000_1));
|
||||
printf(" %u => %u\n", CODEC_PCMA_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_PCMA_8000_1));
|
||||
printf(" %u => %u\n", CODEC_G729_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_G729_8000_1));
|
||||
printf(" %u <= %u\n", CODEC_PCMU_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_PCMU_8000_1));
|
||||
printf(" %u <= %u\n", CODEC_GSM_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_GSM_8000_1));
|
||||
printf(" %u <= %u\n", CODEC_PCMA_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_PCMA_8000_1));
|
||||
printf(" %u <= %u\n", CODEC_G729_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_G729_8000_1));
|
||||
printf("\n");
|
||||
|
||||
/* Try to do mappings from statically defined range to danymic range and vice versa. This
|
||||
* is illegal and should result into a 1:1 mapping */
|
||||
ptmap[3].codec = CODEC_AMRWB_16000_1;
|
||||
ptmap[3].pt = 2;
|
||||
ptmap[4].codec = CODEC_PCMU_8000_1;
|
||||
ptmap[4].pt = 100;
|
||||
ptmap_len = 5;
|
||||
|
||||
/* Apply all mappings again, the illegal ones we defined should result into 1:1 mappings */
|
||||
for (i = 0; i < ptmap_len; i++)
|
||||
printf(" %u => %u\n", ptmap[i].codec, map_codec_to_pt(ptmap, ptmap_len, ptmap[i].codec));
|
||||
for (i = 0; i < ptmap_len; i++)
|
||||
printf(" %u <= %u\n", ptmap[i].pt, map_pt_to_codec(ptmap, ptmap_len, ptmap[i].pt));
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void test_mgcp_client_e1_epname(void)
|
||||
{
|
||||
char *epname;
|
||||
@@ -857,6 +908,7 @@ int main(int argc, char **argv)
|
||||
test_mgcp_msg();
|
||||
test_mgcp_client_cancel();
|
||||
test_sdp_section_start();
|
||||
test_map_codec_to_pt_and_map_pt_to_codec();
|
||||
test_map_str_to_codec();
|
||||
test_mgcp_client_e1_epname();
|
||||
|
||||
|
||||
@@ -128,6 +128,10 @@ test_sdp_section_start() test [18]:
|
||||
body: "some mgcp header data\r\nand header params\r\n\r\nc=IN IP4 \r\n"
|
||||
DLMGCP Failed to parse MGCP response header (audio ip)
|
||||
got rc=-22
|
||||
DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2
|
||||
DLMGCP ptmap contains illegal mapping: codec=0 maps to pt=100
|
||||
DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2
|
||||
DLMGCP ptmap contains illegal mapping: codec=0 maps to pt=100
|
||||
DLMGCP MGW(mgw) MGCP client: using endpoint domain '@mgw'
|
||||
DLMGCP MGW(mgw) Cannot compose MGCP e1-endpoint name (ds/e1-15/s-1/su128-0@mgw), rate(128)/offset(0) combination is invalid!
|
||||
DLMGCP MGW(mgw) Cannot compose MGCP e1-endpoint name (ds/e1-15/s-1/su8-16@mgw), rate(8)/offset(16) combination is invalid!
|
||||
|
||||
@@ -181,6 +181,35 @@ test_sdp_section_start() test [16]:
|
||||
test_sdp_section_start() test [17]:
|
||||
|
||||
test_sdp_section_start() test [18]:
|
||||
110 => 96
|
||||
111 => 97
|
||||
112 => 98
|
||||
113 => 99
|
||||
96 <= 110
|
||||
97 <= 111
|
||||
98 <= 112
|
||||
99 <= 113
|
||||
|
||||
0 => 0
|
||||
3 => 3
|
||||
8 => 8
|
||||
18 => 18
|
||||
0 <= 0
|
||||
3 <= 3
|
||||
8 <= 8
|
||||
18 <= 18
|
||||
|
||||
110 => 96
|
||||
111 => 97
|
||||
112 => 98
|
||||
113 => 113
|
||||
0 => 0
|
||||
96 <= 110
|
||||
97 <= 111
|
||||
98 <= 112
|
||||
2 <= 2
|
||||
100 <= 100
|
||||
|
||||
ds/e1-1/s-15/su64-0@mgw
|
||||
ds/e1-2/s-14/su32-0@mgw
|
||||
ds/e1-3/s-13/su32-4@mgw
|
||||
|
||||
63
tests/sdp/Makefile.am
Normal file
63
tests/sdp/Makefile.am
Normal file
@@ -0,0 +1,63 @@
|
||||
AM_CPPFLAGS = \
|
||||
$(all_includes) \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(top_builddir)/include \
|
||||
-I$(top_srcdir) \
|
||||
$(NULL)
|
||||
|
||||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
-ggdb3 \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(COVERAGE_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
AM_LDFLAGS = \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(COVERAGE_LDFLAGS) \
|
||||
-no-install \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_DIST = \
|
||||
sdp_fmtp_test.ok \
|
||||
sdp_fmtp_test.err \
|
||||
sdp_codec_test.ok \
|
||||
sdp_codec_test.err \
|
||||
$(NULL)
|
||||
|
||||
check_PROGRAMS = \
|
||||
sdp_fmtp_test \
|
||||
sdp_codec_test \
|
||||
sdp_msg_test \
|
||||
$(NULL)
|
||||
|
||||
sdp_fmtp_test_SOURCES = \
|
||||
sdp_fmtp_test.c \
|
||||
$(NULL)
|
||||
|
||||
sdp_fmtp_test_LDADD = \
|
||||
$(top_builddir)/src/libosmo-sdp/libosmo-sdp.la \
|
||||
$(NULL)
|
||||
|
||||
sdp_codec_test_SOURCES = \
|
||||
sdp_codec_test.c \
|
||||
$(NULL)
|
||||
|
||||
sdp_codec_test_LDADD = \
|
||||
$(top_builddir)/src/libosmo-sdp/libosmo-sdp.la \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
sdp_msg_test_SOURCES = \
|
||||
sdp_msg_test.c \
|
||||
$(NULL)
|
||||
|
||||
sdp_msg_test_LDADD = \
|
||||
$(top_builddir)/src/libosmo-sdp/libosmo-sdp.la \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
update_exp:
|
||||
$(builddir)/sdp_fmtp_test >$(srcdir)/sdp_fmtp_test.ok 2>$(srcdir)/sdp_fmtp_test.err
|
||||
$(builddir)/sdp_codec_test >$(srcdir)/sdp_codec_test.ok 2>$(srcdir)/sdp_codec_test.err
|
||||
$(builddir)/sdp_msg_test >$(srcdir)/sdp_msg_test.ok 2>$(srcdir)/sdp_msg_test.err
|
||||
645
tests/sdp/sdp_codec_test.c
Normal file
645
tests/sdp/sdp_codec_test.c
Normal file
@@ -0,0 +1,645 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
|
||||
#include <osmocom/sdp/sdp_codec_list.h>
|
||||
#include <osmocom/sdp/fmtp.h>
|
||||
|
||||
void *test_ctx = NULL;
|
||||
|
||||
static void report_callback(const void *ptr, int depth, int max_depth, int is_ref, void *priv)
|
||||
{
|
||||
const char *name = talloc_get_name(ptr);
|
||||
printf(" |%*s%3zu %s\n", depth, "", talloc_total_blocks(ptr), name);
|
||||
}
|
||||
|
||||
/* Print a talloc report that is reproducible for test output verification. It contains no pointer addresses. */
|
||||
#define report(CTX) _report(CTX, #CTX)
|
||||
static void _report(void *ctx, const char *label)
|
||||
{
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
printf("%s\n", label);
|
||||
talloc_report_depth_cb(ctx, 0, 100, report_callback, NULL);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
struct codec_test {
|
||||
struct osmo_sdp_codec set;
|
||||
int expect_rc;
|
||||
const char *expect_str;
|
||||
bool expect_is_set;
|
||||
};
|
||||
|
||||
struct codec_test codec_tests[] = {
|
||||
{
|
||||
.set = { 23, "encoding-name", 8000, NULL },
|
||||
.expect_str = "encoding-name#23",
|
||||
.expect_is_set = true,
|
||||
},
|
||||
{
|
||||
.set = { 112, "AMR", 8000, "octet-align=1;mode-set=0,2,4" },
|
||||
.expect_str = "AMR:octet-align=1;mode-set=0,2,4#112",
|
||||
.expect_is_set = true,
|
||||
},
|
||||
{
|
||||
.set = { 96, "AMR", 8000, "mode-set=0,2,4;octet-align=1" },
|
||||
.expect_str = "AMR:mode-set=0,2,4;octet-align=1#96",
|
||||
.expect_is_set = true,
|
||||
},
|
||||
{
|
||||
.set = { 114, "AMR", 8000, "mode-set=0,2,4" },
|
||||
.expect_str = "AMR:mode-set=0,2,4#114",
|
||||
.expect_is_set = true,
|
||||
},
|
||||
{
|
||||
.set = { 97, "AMR", 8000, "mode-set=0,2,4;octet-align=0" },
|
||||
.expect_str = "AMR:mode-set=0,2,4;octet-align=0#97",
|
||||
.expect_is_set = true,
|
||||
},
|
||||
{
|
||||
.set = { 98, "AMR", 8000, "octet-align=1" },
|
||||
.expect_str = "AMR:octet-align=1#98",
|
||||
.expect_is_set = true,
|
||||
},
|
||||
{
|
||||
.set = { 96, "AMR-WB", 16000 },
|
||||
.expect_str = "AMR-WB/16000#96",
|
||||
.expect_is_set = true,
|
||||
},
|
||||
{
|
||||
.set = { 3, "GSM", 8000 },
|
||||
.expect_str = "GSM#3",
|
||||
.expect_is_set = true,
|
||||
},
|
||||
{
|
||||
.set = { },
|
||||
.expect_str = "/0#0",
|
||||
.expect_is_set = false,
|
||||
},
|
||||
{
|
||||
.set = { 112, NULL, 8000, "octet-align=1" },
|
||||
.expect_str = ":octet-align=1#112",
|
||||
.expect_is_set = false,
|
||||
},
|
||||
{
|
||||
.set = { 112, "", 8000, "octet-align=1" },
|
||||
.expect_str = ":octet-align=1#112",
|
||||
.expect_is_set = false,
|
||||
},
|
||||
};
|
||||
|
||||
void test_codec(void)
|
||||
{
|
||||
void *ctx = talloc_named_const(test_ctx, 0, __func__);
|
||||
|
||||
struct codec_test *t;
|
||||
struct codec_test *t2;
|
||||
|
||||
printf("\n\n--- %s()\n", __func__);
|
||||
printf("- osmo_sdp_codec_set():\n");
|
||||
for (t = codec_tests; (t - codec_tests) < ARRAY_SIZE(codec_tests); t++) {
|
||||
struct osmo_sdp_codec *codec = osmo_sdp_codec_alloc(ctx);
|
||||
char *str;
|
||||
bool is_set;
|
||||
|
||||
osmo_sdp_codec_set(codec, t->set.payload_type, t->set.encoding_name, t->set.rate, t->set.fmtp);
|
||||
|
||||
str = osmo_sdp_codec_to_str_c(ctx, codec);
|
||||
printf("osmo_sdp_codec_set [%d] '%s'\n", (int)(t - codec_tests), str);
|
||||
if (strcmp(str, t->expect_str))
|
||||
printf(" *** ERROR: expected '%s'\n", t->expect_str);
|
||||
|
||||
if (!osmo_sdp_codec_cmp(codec, &t->set, &osmo_sdp_codec_cmp_exact))
|
||||
printf(" osmo_sdp_codec_cmp() ok\n");
|
||||
else
|
||||
printf(" osmo_sdp_codec_cmp() *** ERROR: mismatches original values\n");
|
||||
|
||||
is_set = osmo_sdp_codec_is_set(codec);
|
||||
printf(" osmo_sdp_codec_is_set() = %s\n", is_set ? "true" : "false");
|
||||
if (is_set != t->expect_is_set)
|
||||
printf(" *** ERROR: expected is_set = %s\n", t->expect_is_set ? "true" : "false");
|
||||
|
||||
if (is_set != osmo_sdp_codec_is_set(&t->set))
|
||||
printf(" *** ERROR: is_set(copy) != is_set(orig)\n");
|
||||
|
||||
talloc_free(str);
|
||||
talloc_free(codec);
|
||||
if (talloc_total_blocks(ctx) != 1)
|
||||
printf(" *** ERROR: ctx has %zu items, should be 1\n", talloc_total_blocks(ctx));
|
||||
}
|
||||
|
||||
printf("\n- osmo_sdp_codec_cmp(equivalent):\n");
|
||||
for (t = codec_tests; (t - codec_tests) < ARRAY_SIZE(codec_tests); t++) {
|
||||
for (t2 = codec_tests; (t2 - codec_tests) < ARRAY_SIZE(codec_tests); t2++) {
|
||||
int cmp = osmo_sdp_codec_cmp(&t->set, &t2->set, &osmo_sdp_codec_cmp_equivalent);
|
||||
int reverse_cmp = osmo_sdp_codec_cmp(&t2->set, &t->set, &osmo_sdp_codec_cmp_equivalent);
|
||||
printf(" %s %s %s %s %s\n",
|
||||
osmo_sdp_codec_to_str_c(ctx, &t->set),
|
||||
(cmp == 0) ? "=="
|
||||
: ((cmp < 0) ? "<" : ">"),
|
||||
osmo_sdp_codec_to_str_c(ctx, &t2->set),
|
||||
(reverse_cmp == 0) ? "=="
|
||||
: ((reverse_cmp < 0) ? "<" : ">"),
|
||||
osmo_sdp_codec_to_str_c(ctx, &t->set));
|
||||
|
||||
if (reverse_cmp != -cmp)
|
||||
printf(" *** ERROR: osmo_sdp_codec_cmp(reverse args) == %d, expected %d\n",
|
||||
reverse_cmp, -cmp);
|
||||
|
||||
talloc_free_children(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n- osmo_sdp_codec_from_str():\n");
|
||||
for (t = codec_tests; (t - codec_tests) < ARRAY_SIZE(codec_tests); t++) {
|
||||
struct osmo_sdp_codec *codec = osmo_sdp_codec_alloc(ctx);
|
||||
int rc = osmo_sdp_codec_from_str(codec, t->expect_str, -1);
|
||||
printf(" osmo_sdp_codec_from_str('%s') rc=%d",
|
||||
t->expect_str, rc);
|
||||
if (!rc) {
|
||||
printf(" res=%s", osmo_sdp_codec_to_str_c(ctx, codec));
|
||||
rc = osmo_sdp_codec_cmp(codec, &t->set, &osmo_sdp_codec_cmp_exact);
|
||||
if (rc)
|
||||
printf(" *** ERROR: osmo_sdp_codec_cmp(res,orig) = %d", rc);
|
||||
}
|
||||
printf("\n");
|
||||
talloc_free_children(ctx);
|
||||
}
|
||||
|
||||
talloc_free(ctx);
|
||||
}
|
||||
|
||||
void test_codec_list(void)
|
||||
{
|
||||
void *list_ctx = talloc_named_const(test_ctx, 0, __func__);
|
||||
void *print_ctx = talloc_named_const(test_ctx, 0, "print");
|
||||
int i;
|
||||
int rc;
|
||||
struct osmo_sdp_codec *codec;
|
||||
|
||||
const struct osmo_sdp_codec all_codecs[] = {
|
||||
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
|
||||
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
|
||||
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
|
||||
};
|
||||
|
||||
struct osmo_sdp_codec_list *codec_list;
|
||||
|
||||
printf("\n\n--- %s()\n", __func__);
|
||||
|
||||
codec_list = osmo_sdp_codec_list_alloc(list_ctx);
|
||||
printf("osmo_sdp_codec_list_first() = %s\n",
|
||||
osmo_sdp_codec_to_str_c(print_ctx, osmo_sdp_codec_list_first(codec_list)));
|
||||
report(list_ctx);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(all_codecs); i++) {
|
||||
struct osmo_sdp_codec *added = osmo_sdp_codec_list_add(codec_list, &all_codecs[i], NULL, false);
|
||||
printf("[%d] osmo_sdp_codec_list_add(%s)\n", i, osmo_sdp_codec_to_str_c(print_ctx, added));
|
||||
}
|
||||
|
||||
i = 0;
|
||||
osmo_sdp_codec_list_foreach(codec, codec_list) {
|
||||
printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
|
||||
}
|
||||
printf("osmo_sdp_codec_list_first() = %s\n",
|
||||
osmo_sdp_codec_to_str_c(print_ctx, osmo_sdp_codec_list_first(codec_list)));
|
||||
report(list_ctx);
|
||||
|
||||
printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
|
||||
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
|
||||
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
|
||||
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
|
||||
|
||||
printf("\n");
|
||||
printf("- add same entries again with once=exact, nothing should change\n");
|
||||
for (i = 0; i < ARRAY_SIZE(all_codecs); i++) {
|
||||
struct osmo_sdp_codec *added = osmo_sdp_codec_list_add(codec_list, &all_codecs[i],
|
||||
&osmo_sdp_codec_cmp_exact, false);
|
||||
printf("[] osmo_sdp_codec_list_add(%s)\n", osmo_sdp_codec_to_str_c(print_ctx, added));
|
||||
}
|
||||
i = 0;
|
||||
osmo_sdp_codec_list_foreach(codec, codec_list) {
|
||||
printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
|
||||
}
|
||||
report(list_ctx);
|
||||
|
||||
printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
|
||||
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
|
||||
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
|
||||
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
|
||||
|
||||
printf("\n");
|
||||
printf("- add same entries again with once=NULL, duplicates are added\n");
|
||||
for (i = 0; i < ARRAY_SIZE(all_codecs); i++) {
|
||||
struct osmo_sdp_codec *added = osmo_sdp_codec_list_add(codec_list, &all_codecs[i], NULL, false);
|
||||
printf("[] osmo_sdp_codec_list_add(%s)\n", osmo_sdp_codec_to_str_c(print_ctx, added));
|
||||
}
|
||||
i = 0;
|
||||
osmo_sdp_codec_list_foreach(codec, codec_list) {
|
||||
printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
|
||||
}
|
||||
report(list_ctx);
|
||||
|
||||
printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
|
||||
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
|
||||
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
|
||||
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
|
||||
|
||||
printf("\n");
|
||||
printf("- add same entries again with once=NULL,pick_unused_pt_nr=true, duplicates are added with new #nr\n");
|
||||
for (i = 0; i < ARRAY_SIZE(all_codecs); i++) {
|
||||
struct osmo_sdp_codec *added = osmo_sdp_codec_list_add(codec_list, &all_codecs[i], NULL, true);
|
||||
printf("[] osmo_sdp_codec_list_add(%s)\n", osmo_sdp_codec_to_str_c(print_ctx, added));
|
||||
}
|
||||
i = 0;
|
||||
osmo_sdp_codec_list_foreach(codec, codec_list) {
|
||||
printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
|
||||
}
|
||||
report(list_ctx);
|
||||
|
||||
printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
|
||||
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
|
||||
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
|
||||
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
|
||||
|
||||
printf("\n");
|
||||
printf("- remove all 'GSM#3' entries, with osmo_sdp_codec_cmp_exact\n");
|
||||
rc = osmo_sdp_codec_list_remove(codec_list, &all_codecs[1], &osmo_sdp_codec_cmp_exact);
|
||||
printf(" osmo_sdp_codec_list_remove() = %d\n", rc);
|
||||
i = 0;
|
||||
osmo_sdp_codec_list_foreach(codec, codec_list) {
|
||||
printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
|
||||
}
|
||||
report(list_ctx);
|
||||
|
||||
printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
|
||||
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
|
||||
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
|
||||
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
|
||||
|
||||
printf("- remove all 'GSM' entries, with osmo_sdp_codec_cmp_equivalent\n");
|
||||
rc = osmo_sdp_codec_list_remove(codec_list, &all_codecs[1], &osmo_sdp_codec_cmp_equivalent);
|
||||
printf(" osmo_sdp_codec_list_remove() = %d\n", rc);
|
||||
i = 0;
|
||||
osmo_sdp_codec_list_foreach(codec, codec_list) {
|
||||
printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
|
||||
}
|
||||
report(list_ctx);
|
||||
|
||||
printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
|
||||
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
|
||||
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
|
||||
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
|
||||
|
||||
rc = osmo_sdp_codec_list_move_to_first(codec_list, &all_codecs[0], &osmo_sdp_codec_cmp_equivalent);
|
||||
printf("- osmo_sdp_codec_list_move_to_first('%s', equivalent) = %d\n",
|
||||
osmo_sdp_codec_to_str_c(print_ctx, &all_codecs[0]), rc);
|
||||
i = 0;
|
||||
osmo_sdp_codec_list_foreach(codec, codec_list) {
|
||||
printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
|
||||
}
|
||||
report(list_ctx);
|
||||
|
||||
printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
|
||||
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
|
||||
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
|
||||
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
|
||||
|
||||
|
||||
printf("- osmo_sdp_codec_list_free_items()\n");
|
||||
osmo_sdp_codec_list_free_items(codec_list);
|
||||
i = 0;
|
||||
osmo_sdp_codec_list_foreach(codec, codec_list) {
|
||||
printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
|
||||
}
|
||||
printf(" %d entries\n", i);
|
||||
report(list_ctx);
|
||||
|
||||
printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
|
||||
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
|
||||
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
|
||||
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
|
||||
|
||||
talloc_free(print_ctx);
|
||||
talloc_free(list_ctx);
|
||||
}
|
||||
|
||||
static struct osmo_sdp_codec_list *init_codec_list(void *ctx, const struct osmo_sdp_codec *init_array)
|
||||
{
|
||||
struct osmo_sdp_codec_list *dst = osmo_sdp_codec_list_alloc(ctx);
|
||||
for (; osmo_sdp_codec_is_set(init_array); init_array++)
|
||||
osmo_sdp_codec_list_add(dst, init_array, NULL, false);
|
||||
return dst;
|
||||
}
|
||||
|
||||
void test_codec_list_cmp(void)
|
||||
{
|
||||
void *ctx = talloc_named_const(test_ctx, 0, __func__);
|
||||
void *print_ctx = talloc_named_const(test_ctx, 0, "print");
|
||||
int i;
|
||||
|
||||
const struct osmo_sdp_codec codec_a[] = {
|
||||
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
|
||||
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
|
||||
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
|
||||
{}
|
||||
};
|
||||
|
||||
const struct osmo_sdp_codec codec_b[][5] = {
|
||||
/* same */
|
||||
{
|
||||
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
|
||||
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
|
||||
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
|
||||
},
|
||||
|
||||
/* different payload_type */
|
||||
{
|
||||
{ .payload_type = 96, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
|
||||
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
|
||||
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
|
||||
},
|
||||
|
||||
/* AMR fmtp in different order */
|
||||
{
|
||||
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "mode-set=0,2,4;octet-align=1" },
|
||||
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
|
||||
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
|
||||
},
|
||||
|
||||
/* different AMR mode-set */
|
||||
{
|
||||
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=7" },
|
||||
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
|
||||
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
|
||||
},
|
||||
|
||||
/* empty AMR mode-set */
|
||||
{
|
||||
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1" },
|
||||
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
|
||||
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
|
||||
},
|
||||
|
||||
/* different AMR octet-align */
|
||||
{
|
||||
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=0;mode-set=0,2,4" },
|
||||
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
|
||||
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
|
||||
},
|
||||
|
||||
/* omitted AMR octet-align is identical to octet-align=0 */
|
||||
{
|
||||
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "mode-set=0,2,4" },
|
||||
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
|
||||
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
|
||||
},
|
||||
|
||||
/* different order */
|
||||
{
|
||||
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
|
||||
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
|
||||
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
|
||||
},
|
||||
|
||||
/* one less item */
|
||||
{
|
||||
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
|
||||
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
|
||||
},
|
||||
|
||||
/* one more item */
|
||||
{
|
||||
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
|
||||
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
|
||||
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
|
||||
{ .payload_type = 110, .encoding_name = "GSM-EFR", .rate = 8000 },
|
||||
},
|
||||
};
|
||||
|
||||
const struct osmo_sdp_codec_cmp_flags *test_cmpf[] = {
|
||||
&osmo_sdp_codec_cmp_name,
|
||||
&osmo_sdp_codec_cmp_equivalent,
|
||||
&osmo_sdp_codec_cmp_exact,
|
||||
};
|
||||
|
||||
printf("\n\n--- %s()\n", __func__);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(codec_b); i++) {
|
||||
struct osmo_sdp_codec_list *list_a = init_codec_list(ctx, codec_a);
|
||||
struct osmo_sdp_codec_list *list_b = init_codec_list(ctx, codec_b[i]);
|
||||
int j;
|
||||
|
||||
printf("A = %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, list_a, false));
|
||||
printf("B = %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, list_b, false));
|
||||
|
||||
for (j = 0; j < ARRAY_SIZE(test_cmpf); j++) {
|
||||
const struct osmo_sdp_codec_cmp_flags *cmpf = test_cmpf[j];
|
||||
int cmp = osmo_sdp_codec_list_cmp(list_a, list_b, cmpf);
|
||||
int reverse_cmp = osmo_sdp_codec_list_cmp(list_b, list_a, cmpf);
|
||||
printf(" cmpf[%d]: payload_type=%s rate=%s fmtp=%d: A %s B %s A\n",
|
||||
j,
|
||||
cmpf->payload_type ? "true" : "false",
|
||||
cmpf->rate ? "true" : "false",
|
||||
cmpf->fmtp,
|
||||
(cmp == 0) ? "=="
|
||||
: ((cmp < 0) ? "<" : ">"),
|
||||
(reverse_cmp == 0) ? "=="
|
||||
: ((reverse_cmp < 0) ? "<" : ">"));
|
||||
|
||||
if (reverse_cmp != -cmp)
|
||||
printf(" *** ERROR: osmo_sdp_codec_list_cmp(reverse args) == %d, expected %d\n",
|
||||
reverse_cmp, -cmp);
|
||||
}
|
||||
|
||||
talloc_free(list_a);
|
||||
talloc_free(list_b);
|
||||
|
||||
if (talloc_total_blocks(ctx) != 1) {
|
||||
printf("ERROR: memleak:\n");
|
||||
report(ctx);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
talloc_free_children(print_ctx);
|
||||
}
|
||||
|
||||
talloc_free(print_ctx);
|
||||
talloc_free(ctx);
|
||||
}
|
||||
|
||||
void test_codec_list_intersection(void)
|
||||
{
|
||||
void *ctx = talloc_named_const(test_ctx, 0, __func__);
|
||||
void *print_ctx = talloc_named_const(test_ctx, 0, "print");
|
||||
int i;
|
||||
|
||||
const struct osmo_sdp_codec codec_a[] = {
|
||||
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
|
||||
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
|
||||
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
|
||||
{}
|
||||
};
|
||||
|
||||
const struct osmo_sdp_codec codec_b[][5] = {
|
||||
/* same */
|
||||
{
|
||||
{ .payload_type = 96, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
|
||||
{ .payload_type = 97, .encoding_name = "GSM", .rate = 8000 },
|
||||
{ .payload_type = 98, .encoding_name = "GSM-HR-08", .rate = 8000 },
|
||||
},
|
||||
|
||||
/* same in different order */
|
||||
{
|
||||
{ .payload_type = 98, .encoding_name = "GSM-HR-08", .rate = 8000 },
|
||||
{ .payload_type = 96, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
|
||||
{ .payload_type = 97, .encoding_name = "GSM", .rate = 8000 },
|
||||
},
|
||||
|
||||
/* two matches */
|
||||
{
|
||||
{ .payload_type = 98, .encoding_name = "GSM-HR-08", .rate = 8000 },
|
||||
{ .payload_type = 110, .encoding_name = "GSM-EFR", .rate = 8000 },
|
||||
{ .payload_type = 97, .encoding_name = "GSM", .rate = 8000 },
|
||||
},
|
||||
|
||||
/* no match */
|
||||
{
|
||||
{ .payload_type = 97, .encoding_name = "AMR-WB", .rate = 16000 },
|
||||
{ .payload_type = 98, .encoding_name = "FOO", .rate = 8000 },
|
||||
{ .payload_type = 110, .encoding_name = "GSM-EFR", .rate = 8000 },
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
printf("\n\n--- %s()\n", __func__);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(codec_b); i++) {
|
||||
struct osmo_sdp_codec_list *list_a = init_codec_list(ctx, codec_a);
|
||||
struct osmo_sdp_codec_list *list_b = init_codec_list(ctx, codec_b[i]);
|
||||
|
||||
printf("A = %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, list_a, false));
|
||||
printf("B = %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, list_b, false));
|
||||
|
||||
osmo_sdp_codec_list_intersection(list_a, list_b, &osmo_sdp_codec_cmp_equivalent, false);
|
||||
printf("osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=false)\n = %s\n",
|
||||
osmo_sdp_codec_list_to_str_c(print_ctx, list_a, false));
|
||||
|
||||
talloc_free(list_a);
|
||||
list_a = init_codec_list(ctx, codec_a);
|
||||
|
||||
osmo_sdp_codec_list_intersection(list_a, list_b, &osmo_sdp_codec_cmp_equivalent, true);
|
||||
printf("osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=true)\n = %s\n",
|
||||
osmo_sdp_codec_list_to_str_c(print_ctx, list_a, false));
|
||||
|
||||
talloc_free(list_a);
|
||||
list_a = init_codec_list(ctx, codec_a);
|
||||
|
||||
osmo_sdp_codec_list_intersection(list_b, list_a, &osmo_sdp_codec_cmp_equivalent, false);
|
||||
printf("osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=false)\n = %s\n",
|
||||
osmo_sdp_codec_list_to_str_c(print_ctx, list_b, false));
|
||||
|
||||
talloc_free(list_a);
|
||||
list_a = init_codec_list(ctx, codec_a);
|
||||
|
||||
osmo_sdp_codec_list_intersection(list_b, list_a, &osmo_sdp_codec_cmp_equivalent, true);
|
||||
printf("osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=true)\n = %s\n",
|
||||
osmo_sdp_codec_list_to_str_c(print_ctx, list_b, false));
|
||||
|
||||
talloc_free(list_a);
|
||||
talloc_free(list_b);
|
||||
|
||||
if (talloc_total_blocks(ctx) != 1) {
|
||||
printf("ERROR: memleak:\n");
|
||||
report(ctx);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
talloc_free_children(print_ctx);
|
||||
}
|
||||
|
||||
talloc_free(print_ctx);
|
||||
talloc_free(ctx);
|
||||
}
|
||||
|
||||
|
||||
struct my_obj {
|
||||
struct osmo_sdp_codec *codec;
|
||||
struct osmo_sdp_codec_list *codec_list;
|
||||
};
|
||||
|
||||
struct my_obj *my_obj_alloc(void *ctx)
|
||||
{
|
||||
struct my_obj *o = talloc_zero(ctx, struct my_obj);
|
||||
o->codec_list = osmo_sdp_codec_list_alloc(o);
|
||||
return o;
|
||||
}
|
||||
|
||||
void test_obj_members(void)
|
||||
{
|
||||
void *ctx = talloc_named_const(test_ctx, 0, __func__);
|
||||
void *print_ctx = talloc_named_const(test_ctx, 0, "print");
|
||||
int i;
|
||||
struct osmo_sdp_codec *codec;
|
||||
|
||||
struct my_obj *o;
|
||||
|
||||
printf("\n\n--- %s()\n", __func__);
|
||||
o = my_obj_alloc(ctx);
|
||||
|
||||
o->codec = osmo_sdp_codec_alloc(o);
|
||||
osmo_sdp_codec_set(o->codec, 96, "AMR", 8000, "octet-align=1");
|
||||
|
||||
printf("o->codec = %s\n", osmo_sdp_codec_to_str_c(print_ctx, o->codec));
|
||||
report(ctx);
|
||||
|
||||
osmo_sdp_codec_list_add(o->codec_list, o->codec, false, false);
|
||||
osmo_sdp_codec_list_add(o->codec_list, o->codec, false, true);
|
||||
i = 0;
|
||||
osmo_sdp_codec_list_foreach(codec, o->codec_list) {
|
||||
printf("o->codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
|
||||
}
|
||||
|
||||
report(ctx);
|
||||
printf("talloc_free(o)\n");
|
||||
talloc_free(o);
|
||||
report(ctx);
|
||||
talloc_free(ctx);
|
||||
talloc_free(print_ctx);
|
||||
}
|
||||
|
||||
typedef void (*test_func_t)(void);
|
||||
test_func_t test_func[] = {
|
||||
test_codec,
|
||||
test_codec_list,
|
||||
test_codec_list_cmp,
|
||||
test_codec_list_intersection,
|
||||
test_obj_members,
|
||||
};
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int i;
|
||||
test_ctx = talloc_named_const(NULL, 0, "sdp_codec_test");
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(test_func); i++) {
|
||||
|
||||
test_func[i]();
|
||||
|
||||
if (talloc_total_blocks(test_ctx) != 1) {
|
||||
talloc_report_full(test_ctx, stderr);
|
||||
printf("ERROR after test %d: memory leak\n", i);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
talloc_free(test_ctx);
|
||||
return 0;
|
||||
}
|
||||
0
tests/sdp/sdp_codec_test.err
Normal file
0
tests/sdp/sdp_codec_test.err
Normal file
538
tests/sdp/sdp_codec_test.ok
Normal file
538
tests/sdp/sdp_codec_test.ok
Normal file
@@ -0,0 +1,538 @@
|
||||
|
||||
|
||||
--- test_codec()
|
||||
- osmo_sdp_codec_set():
|
||||
osmo_sdp_codec_set [0] 'encoding-name#23'
|
||||
osmo_sdp_codec_cmp() ok
|
||||
osmo_sdp_codec_is_set() = true
|
||||
osmo_sdp_codec_set [1] 'AMR:octet-align=1;mode-set=0,2,4#112'
|
||||
osmo_sdp_codec_cmp() ok
|
||||
osmo_sdp_codec_is_set() = true
|
||||
osmo_sdp_codec_set [2] 'AMR:mode-set=0,2,4;octet-align=1#96'
|
||||
osmo_sdp_codec_cmp() ok
|
||||
osmo_sdp_codec_is_set() = true
|
||||
osmo_sdp_codec_set [3] 'AMR:mode-set=0,2,4#114'
|
||||
osmo_sdp_codec_cmp() ok
|
||||
osmo_sdp_codec_is_set() = true
|
||||
osmo_sdp_codec_set [4] 'AMR:mode-set=0,2,4;octet-align=0#97'
|
||||
osmo_sdp_codec_cmp() ok
|
||||
osmo_sdp_codec_is_set() = true
|
||||
osmo_sdp_codec_set [5] 'AMR:octet-align=1#98'
|
||||
osmo_sdp_codec_cmp() ok
|
||||
osmo_sdp_codec_is_set() = true
|
||||
osmo_sdp_codec_set [6] 'AMR-WB/16000#96'
|
||||
osmo_sdp_codec_cmp() ok
|
||||
osmo_sdp_codec_is_set() = true
|
||||
osmo_sdp_codec_set [7] 'GSM#3'
|
||||
osmo_sdp_codec_cmp() ok
|
||||
osmo_sdp_codec_is_set() = true
|
||||
osmo_sdp_codec_set [8] '/0#0'
|
||||
osmo_sdp_codec_cmp() ok
|
||||
osmo_sdp_codec_is_set() = false
|
||||
osmo_sdp_codec_set [9] ':octet-align=1#112'
|
||||
osmo_sdp_codec_cmp() ok
|
||||
osmo_sdp_codec_is_set() = false
|
||||
osmo_sdp_codec_set [10] ':octet-align=1#112'
|
||||
osmo_sdp_codec_cmp() ok
|
||||
osmo_sdp_codec_is_set() = false
|
||||
|
||||
- osmo_sdp_codec_cmp(equivalent):
|
||||
encoding-name#23 == encoding-name#23 == encoding-name#23
|
||||
encoding-name#23 > AMR:octet-align=1;mode-set=0,2,4#112 < encoding-name#23
|
||||
encoding-name#23 > AMR:mode-set=0,2,4;octet-align=1#96 < encoding-name#23
|
||||
encoding-name#23 > AMR:mode-set=0,2,4#114 < encoding-name#23
|
||||
encoding-name#23 > AMR:mode-set=0,2,4;octet-align=0#97 < encoding-name#23
|
||||
encoding-name#23 > AMR:octet-align=1#98 < encoding-name#23
|
||||
encoding-name#23 > AMR-WB/16000#96 < encoding-name#23
|
||||
encoding-name#23 > GSM#3 < encoding-name#23
|
||||
encoding-name#23 > /0#0 < encoding-name#23
|
||||
encoding-name#23 > :octet-align=1#112 < encoding-name#23
|
||||
encoding-name#23 > :octet-align=1#112 < encoding-name#23
|
||||
AMR:octet-align=1;mode-set=0,2,4#112 < encoding-name#23 > AMR:octet-align=1;mode-set=0,2,4#112
|
||||
AMR:octet-align=1;mode-set=0,2,4#112 == AMR:octet-align=1;mode-set=0,2,4#112 == AMR:octet-align=1;mode-set=0,2,4#112
|
||||
AMR:octet-align=1;mode-set=0,2,4#112 == AMR:mode-set=0,2,4;octet-align=1#96 == AMR:octet-align=1;mode-set=0,2,4#112
|
||||
AMR:octet-align=1;mode-set=0,2,4#112 > AMR:mode-set=0,2,4#114 < AMR:octet-align=1;mode-set=0,2,4#112
|
||||
AMR:octet-align=1;mode-set=0,2,4#112 > AMR:mode-set=0,2,4;octet-align=0#97 < AMR:octet-align=1;mode-set=0,2,4#112
|
||||
AMR:octet-align=1;mode-set=0,2,4#112 == AMR:octet-align=1#98 == AMR:octet-align=1;mode-set=0,2,4#112
|
||||
AMR:octet-align=1;mode-set=0,2,4#112 < AMR-WB/16000#96 > AMR:octet-align=1;mode-set=0,2,4#112
|
||||
AMR:octet-align=1;mode-set=0,2,4#112 < GSM#3 > AMR:octet-align=1;mode-set=0,2,4#112
|
||||
AMR:octet-align=1;mode-set=0,2,4#112 > /0#0 < AMR:octet-align=1;mode-set=0,2,4#112
|
||||
AMR:octet-align=1;mode-set=0,2,4#112 > :octet-align=1#112 < AMR:octet-align=1;mode-set=0,2,4#112
|
||||
AMR:octet-align=1;mode-set=0,2,4#112 > :octet-align=1#112 < AMR:octet-align=1;mode-set=0,2,4#112
|
||||
AMR:mode-set=0,2,4;octet-align=1#96 < encoding-name#23 > AMR:mode-set=0,2,4;octet-align=1#96
|
||||
AMR:mode-set=0,2,4;octet-align=1#96 == AMR:octet-align=1;mode-set=0,2,4#112 == AMR:mode-set=0,2,4;octet-align=1#96
|
||||
AMR:mode-set=0,2,4;octet-align=1#96 == AMR:mode-set=0,2,4;octet-align=1#96 == AMR:mode-set=0,2,4;octet-align=1#96
|
||||
AMR:mode-set=0,2,4;octet-align=1#96 > AMR:mode-set=0,2,4#114 < AMR:mode-set=0,2,4;octet-align=1#96
|
||||
AMR:mode-set=0,2,4;octet-align=1#96 > AMR:mode-set=0,2,4;octet-align=0#97 < AMR:mode-set=0,2,4;octet-align=1#96
|
||||
AMR:mode-set=0,2,4;octet-align=1#96 == AMR:octet-align=1#98 == AMR:mode-set=0,2,4;octet-align=1#96
|
||||
AMR:mode-set=0,2,4;octet-align=1#96 < AMR-WB/16000#96 > AMR:mode-set=0,2,4;octet-align=1#96
|
||||
AMR:mode-set=0,2,4;octet-align=1#96 < GSM#3 > AMR:mode-set=0,2,4;octet-align=1#96
|
||||
AMR:mode-set=0,2,4;octet-align=1#96 > /0#0 < AMR:mode-set=0,2,4;octet-align=1#96
|
||||
AMR:mode-set=0,2,4;octet-align=1#96 > :octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=1#96
|
||||
AMR:mode-set=0,2,4;octet-align=1#96 > :octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=1#96
|
||||
AMR:mode-set=0,2,4#114 < encoding-name#23 > AMR:mode-set=0,2,4#114
|
||||
AMR:mode-set=0,2,4#114 < AMR:octet-align=1;mode-set=0,2,4#112 > AMR:mode-set=0,2,4#114
|
||||
AMR:mode-set=0,2,4#114 < AMR:mode-set=0,2,4;octet-align=1#96 > AMR:mode-set=0,2,4#114
|
||||
AMR:mode-set=0,2,4#114 == AMR:mode-set=0,2,4#114 == AMR:mode-set=0,2,4#114
|
||||
AMR:mode-set=0,2,4#114 == AMR:mode-set=0,2,4;octet-align=0#97 == AMR:mode-set=0,2,4#114
|
||||
AMR:mode-set=0,2,4#114 < AMR:octet-align=1#98 > AMR:mode-set=0,2,4#114
|
||||
AMR:mode-set=0,2,4#114 < AMR-WB/16000#96 > AMR:mode-set=0,2,4#114
|
||||
AMR:mode-set=0,2,4#114 < GSM#3 > AMR:mode-set=0,2,4#114
|
||||
AMR:mode-set=0,2,4#114 > /0#0 < AMR:mode-set=0,2,4#114
|
||||
AMR:mode-set=0,2,4#114 > :octet-align=1#112 < AMR:mode-set=0,2,4#114
|
||||
AMR:mode-set=0,2,4#114 > :octet-align=1#112 < AMR:mode-set=0,2,4#114
|
||||
AMR:mode-set=0,2,4;octet-align=0#97 < encoding-name#23 > AMR:mode-set=0,2,4;octet-align=0#97
|
||||
AMR:mode-set=0,2,4;octet-align=0#97 < AMR:octet-align=1;mode-set=0,2,4#112 > AMR:mode-set=0,2,4;octet-align=0#97
|
||||
AMR:mode-set=0,2,4;octet-align=0#97 < AMR:mode-set=0,2,4;octet-align=1#96 > AMR:mode-set=0,2,4;octet-align=0#97
|
||||
AMR:mode-set=0,2,4;octet-align=0#97 == AMR:mode-set=0,2,4#114 == AMR:mode-set=0,2,4;octet-align=0#97
|
||||
AMR:mode-set=0,2,4;octet-align=0#97 == AMR:mode-set=0,2,4;octet-align=0#97 == AMR:mode-set=0,2,4;octet-align=0#97
|
||||
AMR:mode-set=0,2,4;octet-align=0#97 < AMR:octet-align=1#98 > AMR:mode-set=0,2,4;octet-align=0#97
|
||||
AMR:mode-set=0,2,4;octet-align=0#97 < AMR-WB/16000#96 > AMR:mode-set=0,2,4;octet-align=0#97
|
||||
AMR:mode-set=0,2,4;octet-align=0#97 < GSM#3 > AMR:mode-set=0,2,4;octet-align=0#97
|
||||
AMR:mode-set=0,2,4;octet-align=0#97 > /0#0 < AMR:mode-set=0,2,4;octet-align=0#97
|
||||
AMR:mode-set=0,2,4;octet-align=0#97 > :octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=0#97
|
||||
AMR:mode-set=0,2,4;octet-align=0#97 > :octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=0#97
|
||||
AMR:octet-align=1#98 < encoding-name#23 > AMR:octet-align=1#98
|
||||
AMR:octet-align=1#98 == AMR:octet-align=1;mode-set=0,2,4#112 == AMR:octet-align=1#98
|
||||
AMR:octet-align=1#98 == AMR:mode-set=0,2,4;octet-align=1#96 == AMR:octet-align=1#98
|
||||
AMR:octet-align=1#98 > AMR:mode-set=0,2,4#114 < AMR:octet-align=1#98
|
||||
AMR:octet-align=1#98 > AMR:mode-set=0,2,4;octet-align=0#97 < AMR:octet-align=1#98
|
||||
AMR:octet-align=1#98 == AMR:octet-align=1#98 == AMR:octet-align=1#98
|
||||
AMR:octet-align=1#98 < AMR-WB/16000#96 > AMR:octet-align=1#98
|
||||
AMR:octet-align=1#98 < GSM#3 > AMR:octet-align=1#98
|
||||
AMR:octet-align=1#98 > /0#0 < AMR:octet-align=1#98
|
||||
AMR:octet-align=1#98 > :octet-align=1#112 < AMR:octet-align=1#98
|
||||
AMR:octet-align=1#98 > :octet-align=1#112 < AMR:octet-align=1#98
|
||||
AMR-WB/16000#96 < encoding-name#23 > AMR-WB/16000#96
|
||||
AMR-WB/16000#96 > AMR:octet-align=1;mode-set=0,2,4#112 < AMR-WB/16000#96
|
||||
AMR-WB/16000#96 > AMR:mode-set=0,2,4;octet-align=1#96 < AMR-WB/16000#96
|
||||
AMR-WB/16000#96 > AMR:mode-set=0,2,4#114 < AMR-WB/16000#96
|
||||
AMR-WB/16000#96 > AMR:mode-set=0,2,4;octet-align=0#97 < AMR-WB/16000#96
|
||||
AMR-WB/16000#96 > AMR:octet-align=1#98 < AMR-WB/16000#96
|
||||
AMR-WB/16000#96 == AMR-WB/16000#96 == AMR-WB/16000#96
|
||||
AMR-WB/16000#96 < GSM#3 > AMR-WB/16000#96
|
||||
AMR-WB/16000#96 > /0#0 < AMR-WB/16000#96
|
||||
AMR-WB/16000#96 > :octet-align=1#112 < AMR-WB/16000#96
|
||||
AMR-WB/16000#96 > :octet-align=1#112 < AMR-WB/16000#96
|
||||
GSM#3 < encoding-name#23 > GSM#3
|
||||
GSM#3 > AMR:octet-align=1;mode-set=0,2,4#112 < GSM#3
|
||||
GSM#3 > AMR:mode-set=0,2,4;octet-align=1#96 < GSM#3
|
||||
GSM#3 > AMR:mode-set=0,2,4#114 < GSM#3
|
||||
GSM#3 > AMR:mode-set=0,2,4;octet-align=0#97 < GSM#3
|
||||
GSM#3 > AMR:octet-align=1#98 < GSM#3
|
||||
GSM#3 > AMR-WB/16000#96 < GSM#3
|
||||
GSM#3 == GSM#3 == GSM#3
|
||||
GSM#3 > /0#0 < GSM#3
|
||||
GSM#3 > :octet-align=1#112 < GSM#3
|
||||
GSM#3 > :octet-align=1#112 < GSM#3
|
||||
/0#0 < encoding-name#23 > /0#0
|
||||
/0#0 < AMR:octet-align=1;mode-set=0,2,4#112 > /0#0
|
||||
/0#0 < AMR:mode-set=0,2,4;octet-align=1#96 > /0#0
|
||||
/0#0 < AMR:mode-set=0,2,4#114 > /0#0
|
||||
/0#0 < AMR:mode-set=0,2,4;octet-align=0#97 > /0#0
|
||||
/0#0 < AMR:octet-align=1#98 > /0#0
|
||||
/0#0 < AMR-WB/16000#96 > /0#0
|
||||
/0#0 < GSM#3 > /0#0
|
||||
/0#0 == /0#0 == /0#0
|
||||
/0#0 < :octet-align=1#112 > /0#0
|
||||
/0#0 < :octet-align=1#112 > /0#0
|
||||
:octet-align=1#112 < encoding-name#23 > :octet-align=1#112
|
||||
:octet-align=1#112 < AMR:octet-align=1;mode-set=0,2,4#112 > :octet-align=1#112
|
||||
:octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=1#96 > :octet-align=1#112
|
||||
:octet-align=1#112 < AMR:mode-set=0,2,4#114 > :octet-align=1#112
|
||||
:octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=0#97 > :octet-align=1#112
|
||||
:octet-align=1#112 < AMR:octet-align=1#98 > :octet-align=1#112
|
||||
:octet-align=1#112 < AMR-WB/16000#96 > :octet-align=1#112
|
||||
:octet-align=1#112 < GSM#3 > :octet-align=1#112
|
||||
:octet-align=1#112 > /0#0 < :octet-align=1#112
|
||||
:octet-align=1#112 == :octet-align=1#112 == :octet-align=1#112
|
||||
:octet-align=1#112 == :octet-align=1#112 == :octet-align=1#112
|
||||
:octet-align=1#112 < encoding-name#23 > :octet-align=1#112
|
||||
:octet-align=1#112 < AMR:octet-align=1;mode-set=0,2,4#112 > :octet-align=1#112
|
||||
:octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=1#96 > :octet-align=1#112
|
||||
:octet-align=1#112 < AMR:mode-set=0,2,4#114 > :octet-align=1#112
|
||||
:octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=0#97 > :octet-align=1#112
|
||||
:octet-align=1#112 < AMR:octet-align=1#98 > :octet-align=1#112
|
||||
:octet-align=1#112 < AMR-WB/16000#96 > :octet-align=1#112
|
||||
:octet-align=1#112 < GSM#3 > :octet-align=1#112
|
||||
:octet-align=1#112 > /0#0 < :octet-align=1#112
|
||||
:octet-align=1#112 == :octet-align=1#112 == :octet-align=1#112
|
||||
:octet-align=1#112 == :octet-align=1#112 == :octet-align=1#112
|
||||
|
||||
- osmo_sdp_codec_from_str():
|
||||
osmo_sdp_codec_from_str('encoding-name#23') rc=0 res=encoding-name#23
|
||||
osmo_sdp_codec_from_str('AMR:octet-align=1;mode-set=0,2,4#112') rc=0 res=AMR:octet-align=1;mode-set=0,2,4#112
|
||||
osmo_sdp_codec_from_str('AMR:mode-set=0,2,4;octet-align=1#96') rc=0 res=AMR:mode-set=0,2,4;octet-align=1#96
|
||||
osmo_sdp_codec_from_str('AMR:mode-set=0,2,4#114') rc=0 res=AMR:mode-set=0,2,4#114
|
||||
osmo_sdp_codec_from_str('AMR:mode-set=0,2,4;octet-align=0#97') rc=0 res=AMR:mode-set=0,2,4;octet-align=0#97
|
||||
osmo_sdp_codec_from_str('AMR:octet-align=1#98') rc=0 res=AMR:octet-align=1#98
|
||||
osmo_sdp_codec_from_str('AMR-WB/16000#96') rc=0 res=AMR-WB/16000#96
|
||||
osmo_sdp_codec_from_str('GSM#3') rc=0 res=GSM#3
|
||||
osmo_sdp_codec_from_str('/0#0') rc=0 res=/0#0
|
||||
osmo_sdp_codec_from_str(':octet-align=1#112') rc=0 res=:octet-align=1#112
|
||||
osmo_sdp_codec_from_str(':octet-align=1#112') rc=0 res=:octet-align=1#112
|
||||
|
||||
|
||||
--- test_codec_list()
|
||||
osmo_sdp_codec_list_first() = NULL
|
||||
list_ctx
|
||||
| 2 test_codec_list
|
||||
| 1 struct osmo_sdp_codec_list
|
||||
[0] osmo_sdp_codec_list_add(AMR:octet-align=1;mode-set=0,2,4#112)
|
||||
[1] osmo_sdp_codec_list_add(GSM#3)
|
||||
[2] osmo_sdp_codec_list_add(GSM-HR-08#111)
|
||||
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
|
||||
codec_list[1] = GSM#3
|
||||
codec_list[2] = GSM-HR-08#111
|
||||
osmo_sdp_codec_list_first() = AMR:octet-align=1;mode-set=0,2,4#112
|
||||
list_ctx
|
||||
| 9 test_codec_list
|
||||
| 8 struct osmo_sdp_codec_list
|
||||
| 2 struct osmo_sdp_codec
|
||||
| 1 GSM-HR-08
|
||||
| 2 struct osmo_sdp_codec
|
||||
| 1 GSM
|
||||
| 3 struct osmo_sdp_codec
|
||||
| 1 octet-align=1;mode-set=0,2,4
|
||||
| 1 AMR
|
||||
osmo_sdp_codec_list_to_str_c(summarize=true):
|
||||
'AMR#112 GSM#3 GSM-HR-08#111'
|
||||
osmo_sdp_codec_list_to_str_c(summarize=false):
|
||||
'AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111'
|
||||
|
||||
- add same entries again with once=exact, nothing should change
|
||||
[] osmo_sdp_codec_list_add(AMR:octet-align=1;mode-set=0,2,4#112)
|
||||
[] osmo_sdp_codec_list_add(GSM#3)
|
||||
[] osmo_sdp_codec_list_add(GSM-HR-08#111)
|
||||
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
|
||||
codec_list[1] = GSM#3
|
||||
codec_list[2] = GSM-HR-08#111
|
||||
list_ctx
|
||||
| 9 test_codec_list
|
||||
| 8 struct osmo_sdp_codec_list
|
||||
| 2 struct osmo_sdp_codec
|
||||
| 1 GSM-HR-08
|
||||
| 2 struct osmo_sdp_codec
|
||||
| 1 GSM
|
||||
| 3 struct osmo_sdp_codec
|
||||
| 1 octet-align=1;mode-set=0,2,4
|
||||
| 1 AMR
|
||||
osmo_sdp_codec_list_to_str_c(summarize=true):
|
||||
'AMR#112 GSM#3 GSM-HR-08#111'
|
||||
osmo_sdp_codec_list_to_str_c(summarize=false):
|
||||
'AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111'
|
||||
|
||||
- add same entries again with once=NULL, duplicates are added
|
||||
[] osmo_sdp_codec_list_add(AMR:octet-align=1;mode-set=0,2,4#112)
|
||||
[] osmo_sdp_codec_list_add(GSM#3)
|
||||
[] osmo_sdp_codec_list_add(GSM-HR-08#111)
|
||||
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
|
||||
codec_list[1] = GSM#3
|
||||
codec_list[2] = GSM-HR-08#111
|
||||
codec_list[3] = AMR:octet-align=1;mode-set=0,2,4#112
|
||||
codec_list[4] = GSM#3
|
||||
codec_list[5] = GSM-HR-08#111
|
||||
list_ctx
|
||||
| 16 test_codec_list
|
||||
| 15 struct osmo_sdp_codec_list
|
||||
| 2 struct osmo_sdp_codec
|
||||
| 1 GSM-HR-08
|
||||
| 2 struct osmo_sdp_codec
|
||||
| 1 GSM
|
||||
| 3 struct osmo_sdp_codec
|
||||
| 1 octet-align=1;mode-set=0,2,4
|
||||
| 1 AMR
|
||||
| 2 struct osmo_sdp_codec
|
||||
| 1 GSM-HR-08
|
||||
| 2 struct osmo_sdp_codec
|
||||
| 1 GSM
|
||||
| 3 struct osmo_sdp_codec
|
||||
| 1 octet-align=1;mode-set=0,2,4
|
||||
| 1 AMR
|
||||
osmo_sdp_codec_list_to_str_c(summarize=true):
|
||||
'2*AMR#112 2*GSM#3 2*GSM-HR-08#111'
|
||||
osmo_sdp_codec_list_to_str_c(summarize=false):
|
||||
'AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111'
|
||||
|
||||
- add same entries again with once=NULL,pick_unused_pt_nr=true, duplicates are added with new #nr
|
||||
[] osmo_sdp_codec_list_add(AMR:octet-align=1;mode-set=0,2,4#96)
|
||||
[] osmo_sdp_codec_list_add(GSM#97)
|
||||
[] osmo_sdp_codec_list_add(GSM-HR-08#98)
|
||||
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
|
||||
codec_list[1] = GSM#3
|
||||
codec_list[2] = GSM-HR-08#111
|
||||
codec_list[3] = AMR:octet-align=1;mode-set=0,2,4#112
|
||||
codec_list[4] = GSM#3
|
||||
codec_list[5] = GSM-HR-08#111
|
||||
codec_list[6] = AMR:octet-align=1;mode-set=0,2,4#96
|
||||
codec_list[7] = GSM#97
|
||||
codec_list[8] = GSM-HR-08#98
|
||||
list_ctx
|
||||
| 23 test_codec_list
|
||||
| 22 struct osmo_sdp_codec_list
|
||||
| 2 struct osmo_sdp_codec
|
||||
| 1 GSM-HR-08
|
||||
| 2 struct osmo_sdp_codec
|
||||
| 1 GSM
|
||||
| 3 struct osmo_sdp_codec
|
||||
| 1 octet-align=1;mode-set=0,2,4
|
||||
| 1 AMR
|
||||
| 2 struct osmo_sdp_codec
|
||||
| 1 GSM-HR-08
|
||||
| 2 struct osmo_sdp_codec
|
||||
| 1 GSM
|
||||
| 3 struct osmo_sdp_codec
|
||||
| 1 octet-align=1;mode-set=0,2,4
|
||||
| 1 AMR
|
||||
| 2 struct osmo_sdp_codec
|
||||
| 1 GSM-HR-08
|
||||
| 2 struct osmo_sdp_codec
|
||||
| 1 GSM
|
||||
| 3 struct osmo_sdp_codec
|
||||
| 1 octet-align=1;mode-set=0,2,4
|
||||
| 1 AMR
|
||||
osmo_sdp_codec_list_to_str_c(summarize=true):
|
||||
'3*AMR 3*GSM 3*GSM-HR-08'
|
||||
osmo_sdp_codec_list_to_str_c(summarize=false):
|
||||
'AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98'
|
||||
|
||||
- remove all 'GSM#3' entries, with osmo_sdp_codec_cmp_exact
|
||||
osmo_sdp_codec_list_remove() = 2
|
||||
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
|
||||
codec_list[1] = GSM-HR-08#111
|
||||
codec_list[2] = AMR:octet-align=1;mode-set=0,2,4#112
|
||||
codec_list[3] = GSM-HR-08#111
|
||||
codec_list[4] = AMR:octet-align=1;mode-set=0,2,4#96
|
||||
codec_list[5] = GSM#97
|
||||
codec_list[6] = GSM-HR-08#98
|
||||
list_ctx
|
||||
| 19 test_codec_list
|
||||
| 18 struct osmo_sdp_codec_list
|
||||
| 2 struct osmo_sdp_codec
|
||||
| 1 GSM-HR-08
|
||||
| 2 struct osmo_sdp_codec
|
||||
| 1 GSM
|
||||
| 3 struct osmo_sdp_codec
|
||||
| 1 octet-align=1;mode-set=0,2,4
|
||||
| 1 AMR
|
||||
| 2 struct osmo_sdp_codec
|
||||
| 1 GSM-HR-08
|
||||
| 3 struct osmo_sdp_codec
|
||||
| 1 octet-align=1;mode-set=0,2,4
|
||||
| 1 AMR
|
||||
| 2 struct osmo_sdp_codec
|
||||
| 1 GSM-HR-08
|
||||
| 3 struct osmo_sdp_codec
|
||||
| 1 octet-align=1;mode-set=0,2,4
|
||||
| 1 AMR
|
||||
osmo_sdp_codec_list_to_str_c(summarize=true):
|
||||
'3*AMR 3*GSM-HR-08 GSM#97'
|
||||
osmo_sdp_codec_list_to_str_c(summarize=false):
|
||||
'AMR:octet-align=1;mode-set=0,2,4#112 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98'
|
||||
- remove all 'GSM' entries, with osmo_sdp_codec_cmp_equivalent
|
||||
osmo_sdp_codec_list_remove() = 1
|
||||
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
|
||||
codec_list[1] = GSM-HR-08#111
|
||||
codec_list[2] = AMR:octet-align=1;mode-set=0,2,4#112
|
||||
codec_list[3] = GSM-HR-08#111
|
||||
codec_list[4] = AMR:octet-align=1;mode-set=0,2,4#96
|
||||
codec_list[5] = GSM-HR-08#98
|
||||
list_ctx
|
||||
| 17 test_codec_list
|
||||
| 16 struct osmo_sdp_codec_list
|
||||
| 2 struct osmo_sdp_codec
|
||||
| 1 GSM-HR-08
|
||||
| 3 struct osmo_sdp_codec
|
||||
| 1 octet-align=1;mode-set=0,2,4
|
||||
| 1 AMR
|
||||
| 2 struct osmo_sdp_codec
|
||||
| 1 GSM-HR-08
|
||||
| 3 struct osmo_sdp_codec
|
||||
| 1 octet-align=1;mode-set=0,2,4
|
||||
| 1 AMR
|
||||
| 2 struct osmo_sdp_codec
|
||||
| 1 GSM-HR-08
|
||||
| 3 struct osmo_sdp_codec
|
||||
| 1 octet-align=1;mode-set=0,2,4
|
||||
| 1 AMR
|
||||
osmo_sdp_codec_list_to_str_c(summarize=true):
|
||||
'3*AMR 3*GSM-HR-08'
|
||||
osmo_sdp_codec_list_to_str_c(summarize=false):
|
||||
'AMR:octet-align=1;mode-set=0,2,4#112 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#96 GSM-HR-08#98'
|
||||
- osmo_sdp_codec_list_move_to_first('AMR:octet-align=1;mode-set=0,2,4#112', equivalent) = 3
|
||||
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
|
||||
codec_list[1] = AMR:octet-align=1;mode-set=0,2,4#112
|
||||
codec_list[2] = AMR:octet-align=1;mode-set=0,2,4#96
|
||||
codec_list[3] = GSM-HR-08#111
|
||||
codec_list[4] = GSM-HR-08#111
|
||||
codec_list[5] = GSM-HR-08#98
|
||||
list_ctx
|
||||
| 17 test_codec_list
|
||||
| 16 struct osmo_sdp_codec_list
|
||||
| 2 struct osmo_sdp_codec
|
||||
| 1 GSM-HR-08
|
||||
| 3 struct osmo_sdp_codec
|
||||
| 1 octet-align=1;mode-set=0,2,4
|
||||
| 1 AMR
|
||||
| 2 struct osmo_sdp_codec
|
||||
| 1 GSM-HR-08
|
||||
| 3 struct osmo_sdp_codec
|
||||
| 1 octet-align=1;mode-set=0,2,4
|
||||
| 1 AMR
|
||||
| 2 struct osmo_sdp_codec
|
||||
| 1 GSM-HR-08
|
||||
| 3 struct osmo_sdp_codec
|
||||
| 1 octet-align=1;mode-set=0,2,4
|
||||
| 1 AMR
|
||||
osmo_sdp_codec_list_to_str_c(summarize=true):
|
||||
'3*AMR 3*GSM-HR-08'
|
||||
osmo_sdp_codec_list_to_str_c(summarize=false):
|
||||
'AMR:octet-align=1;mode-set=0,2,4#112 AMR:octet-align=1;mode-set=0,2,4#112 AMR:octet-align=1;mode-set=0,2,4#96 GSM-HR-08#111 GSM-HR-08#111 GSM-HR-08#98'
|
||||
- osmo_sdp_codec_list_free_items()
|
||||
0 entries
|
||||
list_ctx
|
||||
| 2 test_codec_list
|
||||
| 1 struct osmo_sdp_codec_list
|
||||
osmo_sdp_codec_list_to_str_c(summarize=true):
|
||||
'(no-codecs)'
|
||||
osmo_sdp_codec_list_to_str_c(summarize=false):
|
||||
'(no-codecs)'
|
||||
|
||||
|
||||
--- test_codec_list_cmp()
|
||||
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
|
||||
B = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
|
||||
cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
|
||||
cmpf[1]: payload_type=false rate=true fmtp=1: A == B == A
|
||||
cmpf[2]: payload_type=true rate=true fmtp=2: A == B == A
|
||||
|
||||
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
|
||||
B = AMR:octet-align=1;mode-set=0,2,4#96 GSM#3 GSM-HR-08#111
|
||||
cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
|
||||
cmpf[1]: payload_type=false rate=true fmtp=1: A == B == A
|
||||
cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
|
||||
|
||||
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
|
||||
B = AMR:mode-set=0,2,4;octet-align=1#112 GSM#3 GSM-HR-08#111
|
||||
cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
|
||||
cmpf[1]: payload_type=false rate=true fmtp=1: A == B == A
|
||||
cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
|
||||
|
||||
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
|
||||
B = AMR:octet-align=1;mode-set=7#112 GSM#3 GSM-HR-08#111
|
||||
cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
|
||||
cmpf[1]: payload_type=false rate=true fmtp=1: A < B > A
|
||||
cmpf[2]: payload_type=true rate=true fmtp=2: A < B > A
|
||||
|
||||
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
|
||||
B = AMR:octet-align=1#112 GSM#3 GSM-HR-08#111
|
||||
cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
|
||||
cmpf[1]: payload_type=false rate=true fmtp=1: A == B == A
|
||||
cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
|
||||
|
||||
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
|
||||
B = AMR:octet-align=0;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
|
||||
cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
|
||||
cmpf[1]: payload_type=false rate=true fmtp=1: A > B < A
|
||||
cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
|
||||
|
||||
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
|
||||
B = AMR:mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
|
||||
cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
|
||||
cmpf[1]: payload_type=false rate=true fmtp=1: A > B < A
|
||||
cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
|
||||
|
||||
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
|
||||
B = GSM#3 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112
|
||||
cmpf[0]: payload_type=false rate=false fmtp=0: A < B > A
|
||||
cmpf[1]: payload_type=false rate=true fmtp=1: A < B > A
|
||||
cmpf[2]: payload_type=true rate=true fmtp=2: A < B > A
|
||||
|
||||
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
|
||||
B = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3
|
||||
cmpf[0]: payload_type=false rate=false fmtp=0: A > B < A
|
||||
cmpf[1]: payload_type=false rate=true fmtp=1: A > B < A
|
||||
cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
|
||||
|
||||
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
|
||||
B = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111 GSM-EFR#110
|
||||
cmpf[0]: payload_type=false rate=false fmtp=0: A < B > A
|
||||
cmpf[1]: payload_type=false rate=true fmtp=1: A < B > A
|
||||
cmpf[2]: payload_type=true rate=true fmtp=2: A < B > A
|
||||
|
||||
|
||||
|
||||
--- test_codec_list_intersection()
|
||||
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
|
||||
B = AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98
|
||||
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=false)
|
||||
= AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
|
||||
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=true)
|
||||
= AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98
|
||||
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=false)
|
||||
= AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98
|
||||
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=true)
|
||||
= AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
|
||||
|
||||
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
|
||||
B = GSM-HR-08#98 AMR:octet-align=1;mode-set=0,2,4#96 GSM#97
|
||||
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=false)
|
||||
= AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
|
||||
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=true)
|
||||
= AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98
|
||||
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=false)
|
||||
= GSM-HR-08#98 AMR:octet-align=1;mode-set=0,2,4#96 GSM#97
|
||||
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=true)
|
||||
= GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112 GSM#3
|
||||
|
||||
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
|
||||
B = GSM-HR-08#98 GSM-EFR#110 GSM#97
|
||||
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=false)
|
||||
= GSM#3 GSM-HR-08#111
|
||||
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=true)
|
||||
= GSM#97 GSM-HR-08#98
|
||||
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=false)
|
||||
= GSM-HR-08#98 GSM#97
|
||||
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=true)
|
||||
= GSM-HR-08#111 GSM#3
|
||||
|
||||
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
|
||||
B = AMR-WB/16000#97 FOO#98 GSM-EFR#110
|
||||
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=false)
|
||||
= (no-codecs)
|
||||
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=true)
|
||||
= (no-codecs)
|
||||
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=false)
|
||||
= (no-codecs)
|
||||
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=true)
|
||||
= (no-codecs)
|
||||
|
||||
|
||||
|
||||
--- test_obj_members()
|
||||
o->codec = AMR:octet-align=1#96
|
||||
ctx
|
||||
| 6 test_obj_members
|
||||
| 5 struct my_obj
|
||||
| 3 struct osmo_sdp_codec
|
||||
| 1 octet-align=1
|
||||
| 1 AMR
|
||||
| 1 struct osmo_sdp_codec_list
|
||||
o->codec_list[0] = AMR:octet-align=1#96
|
||||
o->codec_list[1] = AMR:octet-align=1#97
|
||||
ctx
|
||||
| 12 test_obj_members
|
||||
| 11 struct my_obj
|
||||
| 3 struct osmo_sdp_codec
|
||||
| 1 octet-align=1
|
||||
| 1 AMR
|
||||
| 7 struct osmo_sdp_codec_list
|
||||
| 3 struct osmo_sdp_codec
|
||||
| 1 octet-align=1
|
||||
| 1 AMR
|
||||
| 3 struct osmo_sdp_codec
|
||||
| 1 octet-align=1
|
||||
| 1 AMR
|
||||
talloc_free(o)
|
||||
ctx
|
||||
| 1 test_obj_members
|
||||
126
tests/sdp/sdp_fmtp_test.c
Normal file
126
tests/sdp/sdp_fmtp_test.c
Normal file
@@ -0,0 +1,126 @@
|
||||
#include <inttypes.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
#include <osmocom/sdp/fmtp.h>
|
||||
|
||||
struct get_val_test {
|
||||
const char *fmtp_string;
|
||||
const char *val_name;
|
||||
bool expect_rc;
|
||||
const char *expect_val;
|
||||
};
|
||||
|
||||
const struct get_val_test get_val_tests[] = {
|
||||
{
|
||||
"foo=123;bar=success;baz=456", "foo",
|
||||
true, "123"
|
||||
},
|
||||
{
|
||||
"foo=123;bar=success;baz=456", "bar",
|
||||
true, "success"
|
||||
},
|
||||
{
|
||||
"foo=123;bar=success;baz=456", "baz",
|
||||
true, "456"
|
||||
},
|
||||
};
|
||||
|
||||
void test_get_val(void)
|
||||
{
|
||||
int i;
|
||||
printf("\n--- %s()\n", __func__);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(get_val_tests); i++) {
|
||||
const struct get_val_test *t = &get_val_tests[i];
|
||||
char val[128] = {};
|
||||
bool rc = osmo_sdp_fmtp_get_val(val, sizeof(val), t->fmtp_string, t->val_name);
|
||||
bool ok;
|
||||
printf("osmo_sdp_fmtp_get_val('%s', '%s') rc=%s",
|
||||
t->fmtp_string, t->val_name,
|
||||
rc ? "true" : "false");
|
||||
if (rc)
|
||||
printf(" val='%s'", val);
|
||||
ok = true;
|
||||
if (rc != t->expect_rc) {
|
||||
printf(" ERROR: expected rc=%s", t->expect_rc ? "true" : "false");
|
||||
ok = false;
|
||||
}
|
||||
if (t->expect_val && strcmp(val, t->expect_val)) {
|
||||
printf(" ERROR: expected val='%s'", t->expect_val);
|
||||
ok = false;
|
||||
}
|
||||
if (ok)
|
||||
printf(" ok");
|
||||
printf("\n");
|
||||
}
|
||||
printf("\n--- %s() DONE\n", __func__);
|
||||
}
|
||||
|
||||
struct get_int_test {
|
||||
const char *fmtp_string;
|
||||
const char *val_name;
|
||||
int64_t defval;
|
||||
int64_t expect_rc;
|
||||
};
|
||||
|
||||
const struct get_int_test get_int_tests[] = {
|
||||
{
|
||||
"foo=123;bar=success;baz=456", "foo", -1,
|
||||
123
|
||||
},
|
||||
{
|
||||
"foo=123;bar=success;baz=456", "bar", -1,
|
||||
-1
|
||||
},
|
||||
{
|
||||
"foo=123;bar=success;baz=456", "baz", -1,
|
||||
456
|
||||
},
|
||||
};
|
||||
|
||||
void test_get_int(void)
|
||||
{
|
||||
int i;
|
||||
printf("\n--- %s()\n", __func__);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(get_int_tests); i++) {
|
||||
const struct get_int_test *t = &get_int_tests[i];
|
||||
int64_t rc = osmo_sdp_fmtp_get_int(t->fmtp_string, t->val_name, t->defval);
|
||||
printf("osmo_sdp_fmtp_get_int('%s', '%s') rc=%"PRId64,
|
||||
t->fmtp_string, t->val_name, rc);
|
||||
if (rc != t->expect_rc) {
|
||||
printf(" ERROR: expected rc=%"PRId64, t->expect_rc);
|
||||
}
|
||||
else {
|
||||
printf(" ok");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
printf("\n--- %s() DONE\n", __func__);
|
||||
}
|
||||
|
||||
static const struct log_info_cat log_categories[] = {
|
||||
};
|
||||
|
||||
const struct log_info log_info = {
|
||||
.cat = log_categories,
|
||||
.num_cat = ARRAY_SIZE(log_categories),
|
||||
};
|
||||
|
||||
int main(void)
|
||||
{
|
||||
void *ctx = talloc_named_const(NULL, 1, "sdp_fmtp_test");
|
||||
|
||||
osmo_init_logging2(ctx, &log_info);
|
||||
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
|
||||
log_set_print_timestamp(osmo_stderr_target, 0);
|
||||
log_set_use_color(osmo_stderr_target, 0);
|
||||
log_set_print_category_hex(osmo_stderr_target, 0);
|
||||
log_set_print_category(osmo_stderr_target, 1);
|
||||
|
||||
test_get_val();
|
||||
test_get_int();
|
||||
return 0;
|
||||
}
|
||||
0
tests/sdp/sdp_fmtp_test.err
Normal file
0
tests/sdp/sdp_fmtp_test.err
Normal file
14
tests/sdp/sdp_fmtp_test.ok
Normal file
14
tests/sdp/sdp_fmtp_test.ok
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
--- test_get_val()
|
||||
osmo_sdp_fmtp_get_val('foo=123;bar=success;baz=456', 'foo') rc=true val='123' ok
|
||||
osmo_sdp_fmtp_get_val('foo=123;bar=success;baz=456', 'bar') rc=true val='success' ok
|
||||
osmo_sdp_fmtp_get_val('foo=123;bar=success;baz=456', 'baz') rc=true val='456' ok
|
||||
|
||||
--- test_get_val() DONE
|
||||
|
||||
--- test_get_int()
|
||||
osmo_sdp_fmtp_get_int('foo=123;bar=success;baz=456', 'foo') rc=123 ok
|
||||
osmo_sdp_fmtp_get_int('foo=123;bar=success;baz=456', 'bar') rc=-1 ok
|
||||
osmo_sdp_fmtp_get_int('foo=123;bar=success;baz=456', 'baz') rc=456 ok
|
||||
|
||||
--- test_get_int() DONE
|
||||
769
tests/sdp/sdp_msg_test.c
Normal file
769
tests/sdp/sdp_msg_test.c
Normal file
@@ -0,0 +1,769 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
|
||||
#include <osmocom/sdp/sdp_msg.h>
|
||||
|
||||
void *test_ctx = NULL;
|
||||
|
||||
static void report_callback(const void *ptr, int depth, int max_depth, int is_ref, void *priv)
|
||||
{
|
||||
const char *name = talloc_get_name(ptr);
|
||||
printf(" |%*s%3zu %s\n", depth, "", talloc_total_blocks(ptr), name);
|
||||
}
|
||||
|
||||
/* Print a talloc report that is reproducible for test output verification. It contains no pointer addresses. */
|
||||
#define report(CTX) _report(CTX, #CTX)
|
||||
static void _report(void *ctx, const char *label)
|
||||
{
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
printf("%s\n", label);
|
||||
talloc_report_depth_cb(ctx, 0, 100, report_callback, NULL);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
static void dump_sdp(const char *str, const char *prefix)
|
||||
{
|
||||
while (str && *str) {
|
||||
const char *line_end = str;
|
||||
while (*line_end && *line_end != '\r' && *line_end != '\n')
|
||||
line_end++;
|
||||
while (*line_end == '\r' || *line_end == '\n')
|
||||
line_end++;
|
||||
printf("%s%s\n", prefix, osmo_escape_str(str, line_end - str));
|
||||
str = line_end;
|
||||
}
|
||||
}
|
||||
|
||||
struct sdp_msg_test_data {
|
||||
const char *sdp_input;
|
||||
const char *expect_sdp_str;
|
||||
};
|
||||
|
||||
static const struct sdp_msg_test_data sdp_msg_tests[] = {
|
||||
{
|
||||
"v=0\r\n"
|
||||
"o=- 5628250 5628250 IN IP4 192.168.11.121\r\n"
|
||||
"s=-\r\n"
|
||||
"c=IN IP4 192.168.11.121\r\n"
|
||||
"t=0 0\r\n"
|
||||
"m=audio 10020 RTP/AVP 18 0 2 4 8 96 97 98 100 101\r\n"
|
||||
"a=rtpmap:18 G729/8000\r\n"
|
||||
"a=rtpmap:0 PCMU/8000\r\n"
|
||||
"a=rtpmap:2 G726-32/8000\r\n"
|
||||
"a=rtpmap:4 G723/8000\r\n"
|
||||
"a=rtpmap:8 PCMA/8000\r\n"
|
||||
"a=rtpmap:96 G726-40/8000\r\n"
|
||||
"a=rtpmap:97 G726-24/8000\r\n"
|
||||
"a=rtpmap:98 G726-16/8000\r\n"
|
||||
"a=rtpmap:100 NSE/8000\r\n"
|
||||
"a=fmtp:100 192-193\r\n"
|
||||
"a=rtpmap:101 telephone-event/8000\r\n"
|
||||
"a=fmtp:101 0-15\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
"a=sendrecv\r\n"
|
||||
,
|
||||
},
|
||||
{
|
||||
"v=0\r\n"
|
||||
"o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n"
|
||||
"s=FooBar\r\n"
|
||||
"c=IN IP4 192.168.11.151\r\n"
|
||||
"t=0 0\r\n"
|
||||
"m=audio 16398 RTP/AVP 98\r\n"
|
||||
"a=rtpmap:98 AMR/8000\r\n"
|
||||
"a=fmtp:98 octet-align=1; mode-set=4\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
"a=rtcp:16399 IN IP4 192.168.11.151\r\n"
|
||||
,
|
||||
"v=0\r\n"
|
||||
"o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n"
|
||||
"s=FooBar\r\n"
|
||||
"c=IN IP4 192.168.11.151\r\n"
|
||||
"t=0 0\r\n"
|
||||
"m=audio 16398 RTP/AVP 98\r\n"
|
||||
"a=rtpmap:98 AMR/8000\r\n"
|
||||
"a=fmtp:98 octet-align=1; mode-set=4\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
/* The rtcp line is dropped, not supported yet */
|
||||
},
|
||||
{
|
||||
"v=0\r\n"
|
||||
"o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n"
|
||||
"s=FooBar\r\n"
|
||||
"c=IN IP4 192.168.11.140\r\n"
|
||||
"t=0 0\r\n"
|
||||
"m=audio 30436 RTP/AVP 18 0 4 8 101\r\n"
|
||||
"a=rtpmap:18 G729/8000\r\n"
|
||||
"a=rtpmap:0 PCMU/8000\r\n"
|
||||
"a=rtpmap:4 G723/8000\r\n"
|
||||
"a=rtpmap:8 PCMA/8000\r\n"
|
||||
"a=rtpmap:101 telephone-event/8000\r\n"
|
||||
"a=fmtp:101 0-15\r\n"
|
||||
"a=sendrecv\r\n"
|
||||
"a=rtcp:30437\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
,
|
||||
"v=0\r\n"
|
||||
"o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n"
|
||||
"s=FooBar\r\n"
|
||||
"c=IN IP4 192.168.11.140\r\n"
|
||||
"t=0 0\r\n"
|
||||
"m=audio 30436 RTP/AVP 18 0 4 8 101\r\n"
|
||||
"a=rtpmap:18 G729/8000\r\n"
|
||||
"a=rtpmap:0 PCMU/8000\r\n"
|
||||
"a=rtpmap:4 G723/8000\r\n"
|
||||
"a=rtpmap:8 PCMA/8000\r\n"
|
||||
"a=rtpmap:101 telephone-event/8000\r\n"
|
||||
"a=fmtp:101 0-15\r\n"
|
||||
/* a=sendrecv ends up further below */
|
||||
/* The rtcp line is dropped, not supported yet */
|
||||
"a=ptime:20\r\n"
|
||||
"a=sendrecv\r\n"
|
||||
,
|
||||
},
|
||||
{
|
||||
"v=0\r\n"
|
||||
"o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n"
|
||||
"s=FooBar\r\n"
|
||||
"c=IN IP4 192.168.11.140\r\n"
|
||||
"t=0 0\r\n"
|
||||
"m=audio 30436 RTP/AVP 18 0 4 8 101\r\n"
|
||||
"a=rtpmap:18 G729/8000\r\n"
|
||||
"a=rtpmap:0 PCMU/8000\r\n"
|
||||
"a=rtpmap:4 G723/8000\r\n"
|
||||
"a=rtpmap:8 PCMA/8000\r\n"
|
||||
"a=rtpmap:101 telephone-event/8000\r\n"
|
||||
"a=fmtp:101 0-15\r\n"
|
||||
"a=recvonly\r\n"
|
||||
"a=rtcp:30437\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
,
|
||||
"v=0\r\n"
|
||||
"o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n"
|
||||
"s=FooBar\r\n"
|
||||
"c=IN IP4 192.168.11.140\r\n"
|
||||
"t=0 0\r\n"
|
||||
"m=audio 30436 RTP/AVP 18 0 4 8 101\r\n"
|
||||
"a=rtpmap:18 G729/8000\r\n"
|
||||
"a=rtpmap:0 PCMU/8000\r\n"
|
||||
"a=rtpmap:4 G723/8000\r\n"
|
||||
"a=rtpmap:8 PCMA/8000\r\n"
|
||||
"a=rtpmap:101 telephone-event/8000\r\n"
|
||||
"a=fmtp:101 0-15\r\n"
|
||||
/* a=recvonly ends up further below */
|
||||
/* The rtcp line is dropped, not supported yet */
|
||||
"a=ptime:20\r\n"
|
||||
"a=recvonly\r\n"
|
||||
,
|
||||
},
|
||||
{
|
||||
"v=0\r\n"
|
||||
"o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n"
|
||||
"s=FooBar\r\n"
|
||||
"c=IN IP4 192.168.11.140\r\n"
|
||||
"t=0 0\r\n"
|
||||
"m=audio 30436 RTP/AVP 18 0 4 8 101\r\n"
|
||||
"a=rtpmap:18 G729/8000\r\n"
|
||||
"a=rtpmap:0 PCMU/8000\r\n"
|
||||
"a=rtpmap:4 G723/8000\r\n"
|
||||
"a=rtpmap:8 PCMA/8000\r\n"
|
||||
"a=rtpmap:101 telephone-event/8000\r\n"
|
||||
"a=fmtp:101 0-15\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
"a=sendonly\r\n"
|
||||
,
|
||||
},
|
||||
{
|
||||
"v=0\r\n"
|
||||
"o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n"
|
||||
"s=FooBar\r\n"
|
||||
"c=IN IP4 192.168.11.140\r\n"
|
||||
"t=0 0\r\n"
|
||||
"m=audio 30436 RTP/AVP 18 0 4 8 101\r\n"
|
||||
"a=rtpmap:18 G729/8000\r\n"
|
||||
"a=rtpmap:0 PCMU/8000\r\n"
|
||||
"a=rtpmap:4 G723/8000\r\n"
|
||||
"a=rtpmap:8 PCMA/8000\r\n"
|
||||
"a=rtpmap:101 telephone-event/8000\r\n"
|
||||
"a=fmtp:101 0-15\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
"a=inactive\r\n"
|
||||
,
|
||||
},
|
||||
};
|
||||
|
||||
static void test_parse_and_compose(void)
|
||||
{
|
||||
void *ctx = talloc_named_const(test_ctx, 0, __func__);
|
||||
void *print_ctx = talloc_named_const(test_ctx, 0, "print");
|
||||
int i;
|
||||
bool ok = true;
|
||||
|
||||
printf("\n\n%s\n", __func__);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(sdp_msg_tests); i++) {
|
||||
const struct sdp_msg_test_data *t = &sdp_msg_tests[i];
|
||||
struct osmo_sdp_msg *sdp_msg;
|
||||
char str[1024];
|
||||
const char *expect;
|
||||
struct osmo_sdp_msg_decode_ret ret;
|
||||
|
||||
printf("\n[%d]\n", i);
|
||||
dump_sdp(t->sdp_input, "sdp input: ");
|
||||
|
||||
sdp_msg = osmo_sdp_msg_decode(ctx, t->sdp_input, -1, &ret);
|
||||
|
||||
if (ret.rc) {
|
||||
printf("ERROR: %s at %s\n", strerror(abs(ret.rc)),
|
||||
osmo_quote_cstr_c(print_ctx, ret.error.at_input_str, ret.error.at_input_str_len));
|
||||
ok = false;
|
||||
}
|
||||
printf("parsed SDP message %s\n", osmo_sdp_msg_to_str_c(print_ctx, sdp_msg, false));
|
||||
|
||||
osmo_sdp_msg_encode_buf(str, sizeof(str), sdp_msg);
|
||||
dump_sdp(str, "osmo_sdp_msg_encode_buf: ");
|
||||
expect = t->expect_sdp_str ? : t->sdp_input;
|
||||
if (strcmp(str, expect)) {
|
||||
int j;
|
||||
ok = false;
|
||||
printf("ERROR:\n");
|
||||
dump_sdp(expect, "expect: ");
|
||||
for (j = 0; expect[j]; j++) {
|
||||
if (expect[j] != str[j]) {
|
||||
printf("ERROR at position %d, at:\n", j);
|
||||
dump_sdp(str + j, " mismatch: ");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else
|
||||
printf("[%d] ok\n", i);
|
||||
|
||||
report(ctx);
|
||||
printf("talloc_free(sdp_msg)\n");
|
||||
talloc_free(sdp_msg);
|
||||
report(ctx);
|
||||
|
||||
if (talloc_total_blocks(ctx) != 1) {
|
||||
printf("ERROR: memleak\n");
|
||||
talloc_free_children(ctx);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
OSMO_ASSERT(ok);
|
||||
talloc_free(ctx);
|
||||
talloc_free(print_ctx);
|
||||
}
|
||||
|
||||
struct intersect_test_data {
|
||||
const char *descr;
|
||||
const char *sdp_msg_a;
|
||||
const char *sdp_msg_b;
|
||||
const char *expect_intersection;
|
||||
};
|
||||
|
||||
#define SDP_1 \
|
||||
"v=0\r\n" \
|
||||
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n" \
|
||||
"s=GSM Call\r\n" \
|
||||
"c=IN IP4 23.42.23.42\r\n" \
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 30436 RTP/AVP 112 3 111 110\r\n" \
|
||||
"a=rtpmap:112 AMR/8000\r\n" \
|
||||
"a=fmtp:112 octet-align=1\r\n" \
|
||||
"a=rtpmap:3 GSM/8000\r\n" \
|
||||
"a=rtpmap:111 GSM-HR-08/8000\r\n" \
|
||||
"a=rtpmap:110 GSM-EFR/8000\r\n" \
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define SDP_2 \
|
||||
"v=0\r\n" \
|
||||
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n" \
|
||||
"s=GSM Call\r\n" \
|
||||
"c=IN IP4 23.42.23.42\r\n" \
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 30436 RTP/AVP 112 110\r\n" \
|
||||
"a=rtpmap:112 AMR/8000\r\n" \
|
||||
"a=fmtp:112 octet-align=1\r\n" \
|
||||
"a=rtpmap:110 GSM-EFR/8000\r\n" \
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define SDP_3 \
|
||||
"v=0\r\n" \
|
||||
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n" \
|
||||
"s=GSM Call\r\n" \
|
||||
"c=IN IP4 23.42.23.42\r\n" \
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 30436 RTP/AVP 3 111 123\r\n" \
|
||||
"a=rtpmap:3 GSM/8000\r\n" \
|
||||
"a=rtpmap:111 GSM-HR-08/8000\r\n" \
|
||||
"a=rtpmap:123 FOO/8000\r\n" \
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define SDP_4 \
|
||||
"v=0\r\n" \
|
||||
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n" \
|
||||
"s=GSM Call\r\n" \
|
||||
"c=IN IP4 23.42.23.42\r\n" \
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 30436 RTP/AVP 3 111\r\n" \
|
||||
"a=rtpmap:3 GSM/8000\r\n" \
|
||||
"a=rtpmap:111 GSM-HR-08/8000\r\n" \
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define SDP_5 \
|
||||
"v=0\r\n" \
|
||||
"o=libosmo-sdp 0 0 IN IP4 0.0.0.0\r\n" \
|
||||
"s=GSM Call\r\n" \
|
||||
"c=IN IP4 0.0.0.0\r\n" \
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 0 RTP/AVP 112 113 110 3 111\r\n" \
|
||||
"a=rtpmap:112 AMR/8000\r\n" \
|
||||
"a=fmtp:112 octet-align=1;mode-set=0,1,2,3\r\n" \
|
||||
"a=rtpmap:113 AMR-WB/8000\r\n" \
|
||||
"a=fmtp:113 octet-align=1\r\n" \
|
||||
"a=rtpmap:110 GSM-EFR/8000\r\n" \
|
||||
"a=rtpmap:3 GSM/8000\r\n" \
|
||||
"a=rtpmap:111 GSM-HR-08/8000\r\n" \
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
static const struct intersect_test_data intersect_tests[] = {
|
||||
{
|
||||
.descr = "identical codecs lead to no change",
|
||||
.sdp_msg_a = SDP_1,
|
||||
.sdp_msg_b =
|
||||
"v=0\r\n"
|
||||
"c=IN IP4 5.6.7.8\r\n"
|
||||
"m=audio 12345 RTP/AVP 112 3 111 110\r\n"
|
||||
"a=rtpmap:112 AMR/8000\r\n"
|
||||
"a=fmtp:112 octet-align=1\r\n"
|
||||
"a=rtpmap:3 GSM/8000\r\n"
|
||||
"a=rtpmap:111 GSM-HR-08/8000\r\n"
|
||||
"a=rtpmap:110 GSM-EFR/8000\r\n"
|
||||
,
|
||||
.expect_intersection = SDP_1,
|
||||
},
|
||||
{
|
||||
.descr = "identical codecs in different order also lead to no change",
|
||||
.sdp_msg_a = SDP_1,
|
||||
.sdp_msg_b =
|
||||
"v=0\r\n"
|
||||
"c=IN IP4 5.6.7.8\r\n" \
|
||||
"m=audio 12345 RTP/AVP 3 110 111 112\r\n"
|
||||
"a=rtpmap:3 GSM/8000\r\n"
|
||||
"a=rtpmap:110 GSM-EFR/8000\r\n"
|
||||
"a=rtpmap:111 GSM-HR-08/8000\r\n"
|
||||
"a=rtpmap:112 AMR/8000\r\n"
|
||||
"a=fmtp:112 octet-align=1\r\n"
|
||||
,
|
||||
.expect_intersection = SDP_1,
|
||||
},
|
||||
{
|
||||
.descr = "identical codecs with mismatching payload type numbers also lead to no change",
|
||||
.sdp_msg_a = SDP_1,
|
||||
.sdp_msg_b =
|
||||
"v=0\r\n"
|
||||
"c=IN IP4 5.6.7.8\r\n" \
|
||||
"m=audio 12345 RTP/AVP 96 97 98 99\r\n"
|
||||
"a=rtpmap:96 GSM/8000\r\n"
|
||||
"a=rtpmap:97 GSM-EFR/8000\r\n"
|
||||
"a=rtpmap:98 GSM-HR-08/8000\r\n"
|
||||
"a=rtpmap:99 AMR/8000\r\n"
|
||||
"a=fmtp:99 octet-align=1\r\n"
|
||||
,
|
||||
.expect_intersection = SDP_1,
|
||||
},
|
||||
{
|
||||
.descr = "identical codecs plus some extra codecs also lead to no change" ,
|
||||
.sdp_msg_a = SDP_1,
|
||||
.sdp_msg_b =
|
||||
"v=0\r\n"
|
||||
"c=IN IP4 5.6.7.8\r\n" \
|
||||
"m=audio 12345 RTP/AVP 8 0 96 97 98 99\r\n"
|
||||
"a=rtpmap:8 PCMA/8000\r\n"
|
||||
"a=rtpmap:0 PCMU/8000\r\n"
|
||||
"a=rtpmap:96 GSM/8000\r\n"
|
||||
"a=rtpmap:97 GSM-EFR/8000\r\n"
|
||||
"a=rtpmap:98 GSM-HR-08/8000\r\n"
|
||||
"a=rtpmap:99 AMR/8000\r\n"
|
||||
"a=fmtp:99 octet-align=1\r\n"
|
||||
,
|
||||
.expect_intersection = SDP_1,
|
||||
},
|
||||
{
|
||||
.descr = "some codecs removed",
|
||||
.sdp_msg_a = SDP_1,
|
||||
.sdp_msg_b = SDP_2,
|
||||
.expect_intersection = SDP_2,
|
||||
},
|
||||
{
|
||||
.descr = "other codecs removed",
|
||||
.sdp_msg_a = SDP_1,
|
||||
.sdp_msg_b = SDP_3,
|
||||
.expect_intersection = SDP_4,
|
||||
},
|
||||
{
|
||||
.descr = "all codecs removed",
|
||||
.sdp_msg_a = SDP_1,
|
||||
.sdp_msg_b =
|
||||
"v=0\r\n"
|
||||
"s=empty"
|
||||
,
|
||||
.expect_intersection =
|
||||
"v=0\r\n"
|
||||
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
|
||||
"s=GSM Call\r\n"
|
||||
"c=IN IP4 23.42.23.42\r\n"
|
||||
"t=0 0\r\n"
|
||||
"m=audio 30436 RTP/AVP\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
},
|
||||
{
|
||||
.descr = "some real world test case",
|
||||
.sdp_msg_a = SDP_5,
|
||||
.sdp_msg_b = SDP_5,
|
||||
.expect_intersection = SDP_5,
|
||||
},
|
||||
};
|
||||
|
||||
static const char *sdp_msg_logstr(const struct osmo_sdp_msg *sdp_msg)
|
||||
{
|
||||
static char buf[1024];
|
||||
osmo_sdp_msg_encode_buf(buf, sizeof(buf), sdp_msg);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void test_intersect(void)
|
||||
{
|
||||
int i;
|
||||
bool ok = true;
|
||||
void *ctx = talloc_named_const(test_ctx, 0, __func__);
|
||||
|
||||
printf("\n\n%s\n", __func__);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(intersect_tests); i++) {
|
||||
const struct intersect_test_data *t = &intersect_tests[i];
|
||||
struct osmo_sdp_msg *sdp_msg_a = NULL;
|
||||
struct osmo_sdp_msg *sdp_msg_b = NULL;
|
||||
char str[1024];
|
||||
printf("\n[%d] %s\n", i, t->descr);
|
||||
dump_sdp(t->sdp_msg_a, "SDP A: ");
|
||||
dump_sdp(t->sdp_msg_b, " SDP B: ");
|
||||
|
||||
sdp_msg_a = osmo_sdp_msg_decode(ctx, t->sdp_msg_a, -1, NULL);
|
||||
if (!sdp_msg_a) {
|
||||
printf("ERROR parsing SDP A\n");
|
||||
break;
|
||||
}
|
||||
dump_sdp(sdp_msg_logstr(sdp_msg_a), "parsed SDP A: ");
|
||||
|
||||
struct osmo_sdp_msg_decode_ret r;
|
||||
sdp_msg_b = osmo_sdp_msg_decode(ctx, t->sdp_msg_b, -1, &r);
|
||||
if (!sdp_msg_b) {
|
||||
printf("ERROR parsing SDP B\n");
|
||||
break;
|
||||
}
|
||||
dump_sdp(sdp_msg_logstr(sdp_msg_b), "parsed SDP B: ");
|
||||
|
||||
osmo_sdp_codec_list_intersection(sdp_msg_a->codecs, sdp_msg_b->codecs,
|
||||
&osmo_sdp_codec_cmp_equivalent,
|
||||
false);
|
||||
osmo_sdp_msg_encode_buf(str, sizeof(str), sdp_msg_a);
|
||||
dump_sdp(str, "intersection(a,b): ");
|
||||
if (strcmp(str, t->expect_intersection)) {
|
||||
int j;
|
||||
ok = false;
|
||||
printf("ERROR:\n");
|
||||
dump_sdp(t->expect_intersection, "expect_intersection: ");
|
||||
for (j = 0; t->expect_intersection[j]; j++) {
|
||||
if (t->expect_intersection[j] != str[j]) {
|
||||
printf("ERROR at position %d, at:\n", j);
|
||||
dump_sdp(str + j, " mismatch: ");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else
|
||||
printf("[%d] ok\n", i);
|
||||
|
||||
report(ctx);
|
||||
printf("talloc_free(sdp_msg_a)\n");
|
||||
talloc_free(sdp_msg_a);
|
||||
report(ctx);
|
||||
printf("talloc_free(sdp_msg_b)\n");
|
||||
talloc_free(sdp_msg_b);
|
||||
report(ctx);
|
||||
|
||||
if (talloc_total_blocks(ctx) != 1) {
|
||||
printf("ERROR: memleak\n");
|
||||
talloc_free_children(ctx);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
OSMO_ASSERT(ok);
|
||||
talloc_free(ctx);
|
||||
}
|
||||
|
||||
struct sdp_select_test_data {
|
||||
const char *sdp;
|
||||
const struct osmo_sdp_codec_cmp_flags *cmpf;
|
||||
const struct osmo_sdp_codec select;
|
||||
const char *expect_sdp;
|
||||
};
|
||||
|
||||
static const struct osmo_sdp_codec_cmp_flags pt_only = { .payload_type = true };
|
||||
|
||||
static const struct sdp_select_test_data sdp_select_tests[] = {
|
||||
{
|
||||
"v=0\r\n"
|
||||
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
|
||||
"s=GSM Call\r\n"
|
||||
"c=IN IP4 23.42.23.42\r\n"
|
||||
"t=0 0\r\n"
|
||||
"m=audio 30436 RTP/AVP 112 3 111 110\r\n"
|
||||
"a=rtpmap:112 AMR/8000\r\n"
|
||||
"a=fmtp:112 octet-align=1\r\n"
|
||||
"a=rtpmap:3 GSM/8000\r\n"
|
||||
"a=rtpmap:111 GSM-HR-08/8000\r\n"
|
||||
"a=rtpmap:110 GSM-EFR/8000\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
,
|
||||
&pt_only,
|
||||
{ .payload_type = 112, },
|
||||
NULL
|
||||
},
|
||||
{
|
||||
"v=0\r\n"
|
||||
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
|
||||
"s=GSM Call\r\n"
|
||||
"c=IN IP4 23.42.23.42\r\n"
|
||||
"t=0 0\r\n"
|
||||
"m=audio 30436 RTP/AVP 112 3 111 110\r\n"
|
||||
"a=rtpmap:112 AMR/8000\r\n"
|
||||
"a=fmtp:112 octet-align=1\r\n"
|
||||
"a=rtpmap:3 GSM/8000\r\n"
|
||||
"a=rtpmap:111 GSM-HR-08/8000\r\n"
|
||||
"a=rtpmap:110 GSM-EFR/8000\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
,
|
||||
&pt_only,
|
||||
{ .payload_type = 3, },
|
||||
"v=0\r\n"
|
||||
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
|
||||
"s=GSM Call\r\n"
|
||||
"c=IN IP4 23.42.23.42\r\n"
|
||||
"t=0 0\r\n"
|
||||
"m=audio 30436 RTP/AVP 3 112 111 110\r\n"
|
||||
"a=rtpmap:3 GSM/8000\r\n"
|
||||
"a=rtpmap:112 AMR/8000\r\n"
|
||||
"a=fmtp:112 octet-align=1\r\n"
|
||||
"a=rtpmap:111 GSM-HR-08/8000\r\n"
|
||||
"a=rtpmap:110 GSM-EFR/8000\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
},
|
||||
{
|
||||
"v=0\r\n"
|
||||
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
|
||||
"s=GSM Call\r\n"
|
||||
"c=IN IP4 23.42.23.42\r\n"
|
||||
"t=0 0\r\n"
|
||||
"m=audio 30436 RTP/AVP 112 3 111 110\r\n"
|
||||
"a=rtpmap:112 AMR/8000\r\n"
|
||||
"a=fmtp:112 octet-align=1\r\n"
|
||||
"a=rtpmap:3 GSM/8000\r\n"
|
||||
"a=rtpmap:111 GSM-HR-08/8000\r\n"
|
||||
"a=rtpmap:110 GSM-EFR/8000\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
,
|
||||
&pt_only,
|
||||
{ .payload_type = 111, },
|
||||
"v=0\r\n"
|
||||
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
|
||||
"s=GSM Call\r\n"
|
||||
"c=IN IP4 23.42.23.42\r\n"
|
||||
"t=0 0\r\n"
|
||||
"m=audio 30436 RTP/AVP 111 112 3 110\r\n"
|
||||
"a=rtpmap:111 GSM-HR-08/8000\r\n"
|
||||
"a=rtpmap:112 AMR/8000\r\n"
|
||||
"a=fmtp:112 octet-align=1\r\n"
|
||||
"a=rtpmap:3 GSM/8000\r\n"
|
||||
"a=rtpmap:110 GSM-EFR/8000\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
},
|
||||
{
|
||||
"v=0\r\n"
|
||||
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
|
||||
"s=GSM Call\r\n"
|
||||
"c=IN IP4 23.42.23.42\r\n"
|
||||
"t=0 0\r\n"
|
||||
"m=audio 30436 RTP/AVP 112 3 111 110\r\n"
|
||||
"a=rtpmap:112 AMR/8000\r\n"
|
||||
"a=fmtp:112 octet-align=1\r\n"
|
||||
"a=rtpmap:3 GSM/8000\r\n"
|
||||
"a=rtpmap:111 GSM-HR-08/8000\r\n"
|
||||
"a=rtpmap:110 GSM-EFR/8000\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
,
|
||||
&pt_only,
|
||||
{ .payload_type = 110, },
|
||||
"v=0\r\n"
|
||||
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
|
||||
"s=GSM Call\r\n"
|
||||
"c=IN IP4 23.42.23.42\r\n"
|
||||
"t=0 0\r\n"
|
||||
"m=audio 30436 RTP/AVP 110 112 3 111\r\n"
|
||||
"a=rtpmap:110 GSM-EFR/8000\r\n"
|
||||
"a=rtpmap:112 AMR/8000\r\n"
|
||||
"a=fmtp:112 octet-align=1\r\n"
|
||||
"a=rtpmap:3 GSM/8000\r\n"
|
||||
"a=rtpmap:111 GSM-HR-08/8000\r\n"
|
||||
"a=ptime:20\r\n"
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
static void test_select(void)
|
||||
{
|
||||
int i;
|
||||
bool ok = true;
|
||||
void *ctx = talloc_named_const(test_ctx, 0, __func__);
|
||||
void *print_ctx = talloc_named_const(test_ctx, 0, "print");
|
||||
|
||||
printf("\n\n%s\n", __func__);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(sdp_select_tests); i++) {
|
||||
const struct sdp_select_test_data *t = &sdp_select_tests[i];
|
||||
struct osmo_sdp_msg *sdp_msg;
|
||||
char buf[1024];
|
||||
const char *expect_sdp;
|
||||
|
||||
printf("\n[%d]\n", i);
|
||||
sdp_msg = osmo_sdp_msg_decode(ctx, t->sdp, -1, NULL);
|
||||
if (!sdp_msg) {
|
||||
printf("ERROR parsing SDP\n");
|
||||
break;
|
||||
}
|
||||
printf("SDP: %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, sdp_msg->codecs, false));
|
||||
|
||||
printf("Select: %s\n", osmo_sdp_codec_to_str_c(print_ctx, &t->select));
|
||||
osmo_sdp_codec_list_move_to_first(sdp_msg->codecs, &t->select, t->cmpf);
|
||||
|
||||
printf("SDP: %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, sdp_msg->codecs, false));
|
||||
osmo_sdp_msg_encode_buf(buf, sizeof(buf), sdp_msg);
|
||||
|
||||
expect_sdp = t->expect_sdp ? : t->sdp;
|
||||
if (strcmp(buf, expect_sdp)) {
|
||||
int j;
|
||||
ok = false;
|
||||
printf("ERROR:\n");
|
||||
dump_sdp(buf, "selection result: ");
|
||||
dump_sdp(expect_sdp, "expect result: ");
|
||||
for (j = 0; expect_sdp[j]; j++) {
|
||||
if (expect_sdp[j] != buf[j]) {
|
||||
printf("ERROR at position %d, at:\n", j);
|
||||
dump_sdp(buf + j, " mismatch: ");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else
|
||||
printf("[%d] ok\n", i);
|
||||
|
||||
report(ctx);
|
||||
printf("talloc_free(sdp_msg)\n");
|
||||
talloc_free(sdp_msg);
|
||||
report(ctx);
|
||||
if (talloc_total_blocks(ctx) != 1) {
|
||||
printf("ERROR: memleak\n");
|
||||
talloc_free_children(ctx);
|
||||
}
|
||||
printf("\n");
|
||||
talloc_free_children(print_ctx);
|
||||
}
|
||||
|
||||
OSMO_ASSERT(ok);
|
||||
talloc_free(ctx);
|
||||
talloc_free(print_ctx);
|
||||
}
|
||||
|
||||
|
||||
struct my_obj {
|
||||
struct osmo_sdp_msg *sdp_msg;
|
||||
};
|
||||
|
||||
static struct my_obj *my_obj_alloc(void *ctx)
|
||||
{
|
||||
struct my_obj *o = talloc_zero(ctx, struct my_obj);
|
||||
return o;
|
||||
}
|
||||
|
||||
static void test_obj_members(void)
|
||||
{
|
||||
void *ctx = talloc_named_const(test_ctx, 0, __func__);
|
||||
void *print_ctx = talloc_named_const(test_ctx, 0, "print");
|
||||
int i;
|
||||
|
||||
struct my_obj *o;
|
||||
|
||||
printf("\n\n--- %s()\n", __func__);
|
||||
o = my_obj_alloc(ctx);
|
||||
|
||||
o->sdp_msg = osmo_sdp_msg_alloc(o);
|
||||
|
||||
printf("o->sdp_msg = '%s'\n", osmo_sdp_msg_encode_c(print_ctx, o->sdp_msg));
|
||||
report(ctx);
|
||||
|
||||
const struct osmo_sdp_codec all_codecs[] = {
|
||||
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
|
||||
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
|
||||
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
|
||||
};
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(all_codecs); i++)
|
||||
osmo_sdp_codec_list_add(o->sdp_msg->codecs, &all_codecs[i], false, false);
|
||||
|
||||
printf("o->sdp_msg = '%s'\n", osmo_sdp_msg_encode_c(print_ctx, o->sdp_msg));
|
||||
|
||||
report(ctx);
|
||||
printf("talloc_free(o)\n");
|
||||
talloc_free(o);
|
||||
report(ctx);
|
||||
talloc_free(ctx);
|
||||
talloc_free(print_ctx);
|
||||
}
|
||||
|
||||
typedef void (*test_func_t)(void);
|
||||
|
||||
static const test_func_t test_func[] = {
|
||||
test_parse_and_compose,
|
||||
test_intersect,
|
||||
test_select,
|
||||
test_obj_members,
|
||||
};
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int i;
|
||||
test_ctx = talloc_named_const(NULL, 0, "sdp_codec_test");
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(test_func); i++) {
|
||||
|
||||
test_func[i]();
|
||||
|
||||
if (talloc_total_blocks(test_ctx) != 1) {
|
||||
talloc_report_full(test_ctx, stderr);
|
||||
printf("ERROR after test %d: memory leak\n", i);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
talloc_free(test_ctx);
|
||||
return 0;
|
||||
}
|
||||
0
tests/sdp/sdp_msg_test.err
Normal file
0
tests/sdp/sdp_msg_test.err
Normal file
1321
tests/sdp/sdp_msg_test.ok
Normal file
1321
tests/sdp/sdp_msg_test.ok
Normal file
File diff suppressed because it is too large
Load Diff
@@ -13,3 +13,24 @@ AT_KEYWORDS([mgcp])
|
||||
cat $abs_srcdir/mgcp/mgcp_test.ok > expout
|
||||
AT_CHECK([$abs_top_builddir/tests/mgcp/mgcp_test], [], [expout], [ignore])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([sdp_fmtp])
|
||||
AT_KEYWORDS([sdp_fmtp])
|
||||
cat $abs_srcdir/sdp/sdp_fmtp_test.ok > expout
|
||||
cat $abs_srcdir/sdp/sdp_fmtp_test.err > experr
|
||||
AT_CHECK([$abs_top_builddir/tests/sdp/sdp_fmtp_test], [], [expout], [experr])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([sdp_codec])
|
||||
AT_KEYWORDS([sdp_codec])
|
||||
cat $abs_srcdir/sdp/sdp_codec_test.ok > expout
|
||||
cat $abs_srcdir/sdp/sdp_codec_test.err > experr
|
||||
AT_CHECK([$abs_top_builddir/tests/sdp/sdp_codec_test], [], [expout], [experr])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([sdp_msg])
|
||||
AT_KEYWORDS([sdp_msg])
|
||||
cat $abs_srcdir/sdp/sdp_msg_test.ok > expout
|
||||
cat $abs_srcdir/sdp/sdp_msg_test.err > experr
|
||||
AT_CHECK([$abs_top_builddir/tests/sdp/sdp_msg_test], [], [expout], [experr])
|
||||
AT_CLEANUP
|
||||
|
||||
Reference in New Issue
Block a user