mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
				synced 2025-11-02 21:13:44 +00:00 
			
		
		
		
	Compare commits
	
		
			5 Commits
		
	
	
		
			1.14.0
			...
			neels/fmtp
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					48a1062ecb | ||
| 
						 | 
					e48ae27813 | ||
| 
						 | 
					e878f54acd | ||
| 
						 | 
					e9b19c1735 | ||
| 
						 | 
					997cfdbb39 | 
@@ -189,9 +189,11 @@ AC_OUTPUT(
 | 
			
		||||
    libosmo-mgcp-client.pc
 | 
			
		||||
    include/Makefile
 | 
			
		||||
    include/osmocom/Makefile
 | 
			
		||||
    include/osmocom/sdp/Makefile
 | 
			
		||||
    include/osmocom/mgcp_client/Makefile
 | 
			
		||||
    include/osmocom/mgcp/Makefile
 | 
			
		||||
    src/Makefile
 | 
			
		||||
    src/libosmo-sdp/Makefile
 | 
			
		||||
    src/libosmo-mgcp-client/Makefile
 | 
			
		||||
    src/libosmo-mgcp/Makefile
 | 
			
		||||
    src/osmo-mgw/Makefile
 | 
			
		||||
@@ -199,6 +201,7 @@ AC_OUTPUT(
 | 
			
		||||
    tests/atlocal
 | 
			
		||||
    tests/mgcp_client/Makefile
 | 
			
		||||
    tests/mgcp/Makefile
 | 
			
		||||
    tests/sdp/Makefile
 | 
			
		||||
    doc/Makefile
 | 
			
		||||
    doc/examples/Makefile
 | 
			
		||||
    doc/manuals/Makefile
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,8 @@ nobase_include_HEADERS = \
 | 
			
		||||
	osmocom/mgcp_client/mgcp_client_endpoint_fsm.h \
 | 
			
		||||
	osmocom/mgcp_client/mgcp_client_fsm.h \
 | 
			
		||||
	osmocom/mgcp_client/mgcp_client_pool.h \
 | 
			
		||||
	osmocom/sdp/fmtp.h \
 | 
			
		||||
	osmocom/sdp/sdp_strings.h \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
noinst_HEADERS = \
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
SUBDIRS = \
 | 
			
		||||
	sdp \
 | 
			
		||||
	mgcp_client \
 | 
			
		||||
	mgcp \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,7 @@ struct mgcp_conn_rtp;
 | 
			
		||||
void mgcp_codec_summary(struct mgcp_conn_rtp *conn);
 | 
			
		||||
void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn);
 | 
			
		||||
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param);
 | 
			
		||||
int mgcp_codec_add2(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const char *fmtp);
 | 
			
		||||
int mgcp_codec_decide(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst);
 | 
			
		||||
const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn_rtp *conn,
 | 
			
		||||
								const char *subtype_name, unsigned int match_nr);
 | 
			
		||||
 
 | 
			
		||||
@@ -59,7 +59,7 @@ enum mgcp_x_osmo_ign {
 | 
			
		||||
	MGCP_X_OSMO_IGN_CALLID = 1,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Codec parameters (communicated via SDP/fmtp) */
 | 
			
		||||
/* Deprecated. Use the new fmtp string instead. */
 | 
			
		||||
struct mgcp_codec_param {
 | 
			
		||||
	bool amr_octet_aligned_present;
 | 
			
		||||
	bool amr_octet_aligned;
 | 
			
		||||
 
 | 
			
		||||
@@ -83,8 +83,11 @@ struct mgcp_rtp_codec {
 | 
			
		||||
	char audio_name[64];
 | 
			
		||||
	char subtype_name[64];
 | 
			
		||||
 | 
			
		||||
	/* Deprecated. Use the new fmtp string instead. */
 | 
			
		||||
	bool param_present;
 | 
			
		||||
	struct mgcp_codec_param param;
 | 
			
		||||
 | 
			
		||||
	char fmtp[256];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
 | 
			
		||||
 
 | 
			
		||||
@@ -77,6 +77,9 @@ struct ptmap {
 | 
			
		||||
 | 
			
		||||
	/*! payload type number (96-127) */
 | 
			
		||||
	unsigned int pt;
 | 
			
		||||
 | 
			
		||||
	/*! the MGCP 'a=fmtp:N <...>' string, e.g. "mode-set=1,2,3;octet-align=0". */
 | 
			
		||||
	char fmtp[256];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int ptmap_cmp(const struct ptmap *a, const struct ptmap *b);
 | 
			
		||||
 
 | 
			
		||||
@@ -57,10 +57,11 @@ struct mgcp_conn_peer {
 | 
			
		||||
	 * address is set. If != MGCP_CONN_NONE, force this conn mode. */
 | 
			
		||||
	enum mgcp_connection_mode conn_mode;
 | 
			
		||||
 | 
			
		||||
	/*! If the codec requires additional format parameters (fmtp), those cann be set here, see also
 | 
			
		||||
	 * mgcp_common.h */
 | 
			
		||||
	bool param_present;
 | 
			
		||||
	struct mgcp_codec_param param;
 | 
			
		||||
	/*! Deprectated, use ptmap[].fmtp instead.
 | 
			
		||||
	 * Global codec params. In case the codec requires additional format parameters (fmtp), those can be set
 | 
			
		||||
	 * here, see also mgcp_common.h. The format parameters will be applied on all codecs where applicable. */
 | 
			
		||||
	bool param_present OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use ptmap[].fmtp instead");
 | 
			
		||||
	struct mgcp_codec_param param OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use ptmap[].fmtp instead");
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm_inst *parent_fi, uint32_t parent_term_evt,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										0
									
								
								include/osmocom/sdp/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								include/osmocom/sdp/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										33
									
								
								include/osmocom/sdp/fmtp.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								include/osmocom/sdp/fmtp.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
/* 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);
 | 
			
		||||
							
								
								
									
										36
									
								
								include/osmocom/sdp/sdp_strings.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								include/osmocom/sdp/sdp_strings.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
			
		||||
/* 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 ":"
 | 
			
		||||
 | 
			
		||||
/* Media Direction Attributes "a=recvonly", "a=sendrecv", "a=sendonly", "a=inactive" RFC-8866 6.7. */
 | 
			
		||||
#define OSMO_SDP_STR_RECVONLY "recvonly"
 | 
			
		||||
#define OSMO_SDP_STR_SENDRECV "sendrecv"
 | 
			
		||||
#define OSMO_SDP_STR_SENDONLY "sendonly"
 | 
			
		||||
#define OSMO_SDP_STR_INACTIVE "inactive"
 | 
			
		||||
 | 
			
		||||
/* AMR related tokens */
 | 
			
		||||
 | 
			
		||||
#define OSMO_SDP_STR_AMR_OCTET_ALIGN "octet-align"
 | 
			
		||||
 | 
			
		||||
/*! "octet-align=1" */
 | 
			
		||||
#define OSMO_SDP_STR_AMR_OCTET_ALIGN_1  OSMO_SDP_STR_AMR_OCTET_ALIGN "=1"
 | 
			
		||||
 | 
			
		||||
/*! "octet-align=0".
 | 
			
		||||
 * According to spec [1], "octet-align=0" is identical to omitting 'octet-align' entirely. In Osmocom practice, whether
 | 
			
		||||
 * or not "octet-align=0" is present can make a big difference for osmo-mgw versions 1.12 and older, which do not heed
 | 
			
		||||
 * [1].
 | 
			
		||||
 *
 | 
			
		||||
 * spec [1]: RFC4867, see details in description of osmo_sdp_fmtp_amr_is_octet_aligned().
 | 
			
		||||
 */
 | 
			
		||||
#define OSMO_SDP_STR_AMR_OCTET_ALIGN_0  OSMO_SDP_STR_AMR_OCTET_ALIGN "=0"
 | 
			
		||||
 | 
			
		||||
@@ -15,6 +15,7 @@ AM_CFLAGS = \
 | 
			
		||||
 | 
			
		||||
# Libraries
 | 
			
		||||
SUBDIRS = \
 | 
			
		||||
	libosmo-sdp \
 | 
			
		||||
	libosmo-mgcp-client \
 | 
			
		||||
	libosmo-mgcp \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 
 | 
			
		||||
@@ -40,6 +40,7 @@ libosmo_mgcp_client_la_LDFLAGS = \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
libosmo_mgcp_client_la_LIBADD = \
 | 
			
		||||
	$(top_builddir)/src/libosmo-sdp/libosmo-sdp.la \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 
 | 
			
		||||
@@ -29,6 +29,7 @@
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_client.h>
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_client_internal.h>
 | 
			
		||||
#include <osmocom/sdp/sdp_strings.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/abis/e1_input.h>
 | 
			
		||||
 | 
			
		||||
@@ -388,11 +389,12 @@ static int mgcp_parse_audio_ptime_rtpmap(struct mgcp_response *r, const char *li
 | 
			
		||||
{
 | 
			
		||||
	unsigned int pt;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
	char codec_resp[64];
 | 
			
		||||
	char codec_resp[256];
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
#define A_PTIME "a=ptime:"
 | 
			
		||||
#define A_RTPMAP "a=rtpmap:"
 | 
			
		||||
#define A_PTIME OSMO_SDP_A_PREFIX(OSMO_SDP_STR_PTIME)
 | 
			
		||||
#define A_RTPMAP OSMO_SDP_A_PREFIX(OSMO_SDP_STR_RTPMAP)
 | 
			
		||||
#define A_FMTP OSMO_SDP_A_PREFIX(OSMO_SDP_STR_FMTP)
 | 
			
		||||
 | 
			
		||||
	if (osmo_str_startswith(line, A_PTIME)) {
 | 
			
		||||
		if (sscanf(line, A_PTIME "%u", &r->ptime) != 1) {
 | 
			
		||||
@@ -401,7 +403,7 @@ static int mgcp_parse_audio_ptime_rtpmap(struct mgcp_response *r, const char *li
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
	} else if (osmo_str_startswith(line, A_RTPMAP)) {
 | 
			
		||||
		if (sscanf(line, A_RTPMAP "%d %63s", &pt, codec_resp) != 2) {
 | 
			
		||||
		if (sscanf(line, A_RTPMAP "%d %255s", &pt, codec_resp) != 2) {
 | 
			
		||||
			LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
			     "Failed to parse SDP parameter, invalid rtpmap: %s\n", osmo_quote_str(line, -1));
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
@@ -440,6 +442,42 @@ static int mgcp_parse_audio_ptime_rtpmap(struct mgcp_response *r, const char *li
 | 
			
		||||
			.codec = rc,
 | 
			
		||||
		};
 | 
			
		||||
		r->ptmap_len++;
 | 
			
		||||
 | 
			
		||||
	} else if (osmo_str_startswith(line, A_FMTP)) {
 | 
			
		||||
		if (sscanf(line, A_FMTP "%d %255s", &pt, codec_resp) != 2) {
 | 
			
		||||
			LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
			     "Failed to parse SDP parameter, invalid fmtp: %s\n", osmo_quote_str(line, -1));
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* Earlier, a line like "m=audio 16002 RTP/AVP 98 112 3" established the desired order of payloads, now
 | 
			
		||||
		 * enrich it with actual codec information provided by "a=rtpmap:..." entries.
 | 
			
		||||
		 * For each, find the entry with the right pt number and add the info there. */
 | 
			
		||||
 | 
			
		||||
		for (i = 0; i < r->ptmap_len; i++) {
 | 
			
		||||
			if (r->ptmap[i].pt != pt)
 | 
			
		||||
				continue;
 | 
			
		||||
			OSMO_STRLCPY_ARRAY(r->ptmap[r->ptmap_len].fmtp, codec_resp);
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* No entry was found. This is an error in the MGCP protocol, but let's just add another entry
 | 
			
		||||
		 * anyway, to not make it look like it was never there. */
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "error in MGCP message: 'a=fmtp:%u' has no matching entry in 'm=audio ... %u'\n",
 | 
			
		||||
		     pt, pt);
 | 
			
		||||
		if (r->ptmap_len >= ARRAY_SIZE(r->ptmap)) {
 | 
			
		||||
			LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
			     "cannot parse all codecs: can only store up to %zu rtpmap entries.\n",
 | 
			
		||||
			     ARRAY_SIZE(r->ptmap));
 | 
			
		||||
			return -ENOSPC;
 | 
			
		||||
		}
 | 
			
		||||
		r->ptmap[r->ptmap_len] = (struct ptmap){
 | 
			
		||||
			.pt = pt,
 | 
			
		||||
			.codec = -1,
 | 
			
		||||
		};
 | 
			
		||||
		OSMO_STRLCPY_ARRAY(r->ptmap[r->ptmap_len].fmtp, codec_resp);
 | 
			
		||||
		r->ptmap_len++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
@@ -1377,13 +1415,20 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Add optional codec parameters (fmtp) */
 | 
			
		||||
	if (mgcp_msg->param_present) {
 | 
			
		||||
		for (i = 0; i < mgcp_msg->ptmap_len; i++) {
 | 
			
		||||
			/* The following is only applicable for AMR */
 | 
			
		||||
			if (mgcp_msg->ptmap[i].codec != CODEC_AMR_8000_1
 | 
			
		||||
			    && mgcp_msg->ptmap[i].codec != CODEC_AMRWB_16000_1)
 | 
			
		||||
				continue;
 | 
			
		||||
	for (i = 0; i < mgcp_msg->ptmap_len; i++) {
 | 
			
		||||
		/* Add fmtp string, if any is set. */
 | 
			
		||||
		if (mgcp_msg->ptmap[i].fmtp[0]) {
 | 
			
		||||
			MSGB_PRINTF_OR_RET("a=fmtp:%u %s\r\n", mgcp_msg->ptmap[i].pt, mgcp_msg->ptmap[i].fmtp);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* LEGACY COMPAT. Fill in fmtp with the legacy mgcp_msg->param, if any, when no individual fmtp is set
 | 
			
		||||
		 * on the codec. We only ever supported AMR fmtp in mgcp_msg->param. */
 | 
			
		||||
		if (mgcp_msg->param_present
 | 
			
		||||
		    && (mgcp_msg->ptmap[i].codec == CODEC_AMR_8000_1
 | 
			
		||||
			|| mgcp_msg->ptmap[i].codec == CODEC_AMRWB_16000_1)) {
 | 
			
		||||
			pt = mgcp_msg->ptmap[i].pt;
 | 
			
		||||
 | 
			
		||||
			if (mgcp_msg->param.amr_octet_aligned_present && mgcp_msg->param.amr_octet_aligned)
 | 
			
		||||
				MSGB_PRINTF_OR_RET("a=fmtp:%u octet-align=1\r\n", pt);
 | 
			
		||||
			else if (mgcp_msg->param.amr_octet_aligned_present && !mgcp_msg->param.amr_octet_aligned)
 | 
			
		||||
@@ -1621,5 +1666,8 @@ int ptmap_cmp(const struct ptmap *a, const struct ptmap *b)
 | 
			
		||||
	rc = OSMO_CMP(a->codec, b->codec);
 | 
			
		||||
	if (rc)
 | 
			
		||||
		return rc;
 | 
			
		||||
	return OSMO_CMP(a->pt, b->pt);
 | 
			
		||||
	rc = OSMO_CMP(a->pt, b->pt);
 | 
			
		||||
	if (rc)
 | 
			
		||||
		return rc;
 | 
			
		||||
	return strcmp(a->fmtp, b->fmtp);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -19,15 +19,15 @@ AM_LDFLAGS = \
 | 
			
		||||
	$(COVERAGE_LDFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
noinst_LIBRARIES = \
 | 
			
		||||
	libosmo-mgcp.a \
 | 
			
		||||
noinst_LTLIBRARIES = \
 | 
			
		||||
	libosmo-mgcp.la \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
noinst_HEADERS = \
 | 
			
		||||
	g711common.h \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
libosmo_mgcp_a_SOURCES = \
 | 
			
		||||
libosmo_mgcp_la_SOURCES = \
 | 
			
		||||
	mgcp_protocol.c \
 | 
			
		||||
	mgcp_network.c \
 | 
			
		||||
	mgcp_vty.c \
 | 
			
		||||
@@ -43,3 +43,13 @@ libosmo_mgcp_a_SOURCES = \
 | 
			
		||||
	mgcp_e1.c \
 | 
			
		||||
	mgcp_iuup.c \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
libosmo_mgcp_la_LIBADD = \
 | 
			
		||||
	$(top_builddir)/src/libosmo-sdp/libosmo-sdp.la \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(LIBOSMONETIF_LIBS) \
 | 
			
		||||
	$(LIBOSMOABIS_LIBS) \
 | 
			
		||||
	$(LIBOSMOTRAU_LIBS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,10 @@
 | 
			
		||||
#include <osmocom/mgcp/mgcp_endp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_trunk.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_codec.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/sdp/sdp_strings.h>
 | 
			
		||||
#include <osmocom/sdp/fmtp.h>
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
/* Helper function to dump codec information of a specified codec to a printable
 | 
			
		||||
@@ -116,9 +120,9 @@ void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn)
 | 
			
		||||
 *  \param[out] conn related rtp-connection.
 | 
			
		||||
 *  \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined).
 | 
			
		||||
 *  \param[in] audio_name audio codec name, in uppercase (e.g. "GSM/8000/1").
 | 
			
		||||
 *  \param[in] param optional codec parameters (set to NULL when unused).
 | 
			
		||||
 *  \param[in] fmtp optional codec parameters (set to NULL when unused).
 | 
			
		||||
 *  \returns 0 on success, -EINVAL on failure. */
 | 
			
		||||
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param)
 | 
			
		||||
int mgcp_codec_add2(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const char *fmtp)
 | 
			
		||||
{
 | 
			
		||||
	int rate;
 | 
			
		||||
	int channels;
 | 
			
		||||
@@ -261,12 +265,16 @@ int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *aud
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Copy over optional codec parameters */
 | 
			
		||||
	if (param) {
 | 
			
		||||
		codec->param = *param;
 | 
			
		||||
		codec->param_present = true;
 | 
			
		||||
	} else
 | 
			
		||||
		codec->param_present = false;
 | 
			
		||||
	if (fmtp) {
 | 
			
		||||
		OSMO_STRLCPY_ARRAY(codec->fmtp, fmtp);
 | 
			
		||||
		if (strlen(codec->fmtp) != strlen(fmtp)) {
 | 
			
		||||
			LOGP(DLMGCP, LOGL_ERROR, "fmtp too long: %zu > %zu\n", strlen(fmtp), strlen(codec->fmtp));
 | 
			
		||||
			/* let's just hope what is there is still useful, worst case the call's audio doesn't work */
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* legacy */
 | 
			
		||||
	codec->param_present = false;
 | 
			
		||||
 | 
			
		||||
	conn->end.codecs_assigned++;
 | 
			
		||||
	return 0;
 | 
			
		||||
@@ -276,23 +284,33 @@ error:
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Return true if octet-aligned is set in the given codec. Default to octet-aligned=0, i.e. bandwidth-efficient mode.
 | 
			
		||||
 * See RFC4867 "RTP Payload Format for AMR and AMR-WB" sections "8.1. AMR Media Type Registration" and "8.2. AMR-WB
 | 
			
		||||
 * Media Type Registration":
 | 
			
		||||
 *
 | 
			
		||||
 *    octet-align: Permissible values are 0 and 1.  If 1, octet-aligned
 | 
			
		||||
 *                 operation SHALL be used.  If 0 or if not present,
 | 
			
		||||
 *                 bandwidth-efficient operation is employed.
 | 
			
		||||
 *
 | 
			
		||||
 * https://tools.ietf.org/html/rfc4867
 | 
			
		||||
 */
 | 
			
		||||
/*! Legacy compat, use mgcp_codec_add2() instead to be able to pass any fmtp besides AMR octet-align=1.
 | 
			
		||||
 * Add codec configuration depending on payload type and/or codec name. This
 | 
			
		||||
 *  function uses the input parameters to extrapolate the full codec information.
 | 
			
		||||
 *  \param[out] codec configuration (caller provided memory).
 | 
			
		||||
 *  \param[out] conn related rtp-connection.
 | 
			
		||||
 *  \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined).
 | 
			
		||||
 *  \param[in] audio_name audio codec name, in uppercase (e.g. "GSM/8000/1").
 | 
			
		||||
 *  \param[in] param optional codec parameters (set to NULL when unused).
 | 
			
		||||
 *  \returns 0 on success, -EINVAL on failure. */
 | 
			
		||||
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param)
 | 
			
		||||
{
 | 
			
		||||
	const char *fmtp = NULL;
 | 
			
		||||
	if (param && param->amr_octet_aligned_present)
 | 
			
		||||
		fmtp = (param->amr_octet_aligned ? OSMO_SDP_STR_AMR_OCTET_ALIGN_1 : OSMO_SDP_STR_AMR_OCTET_ALIGN_0);
 | 
			
		||||
 | 
			
		||||
	return mgcp_codec_add2(conn, payload_type, audio_name, fmtp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec)
 | 
			
		||||
{
 | 
			
		||||
	if (!codec->param_present)
 | 
			
		||||
		return false;
 | 
			
		||||
	if (!codec->param.amr_octet_aligned_present)
 | 
			
		||||
		return false;
 | 
			
		||||
	return codec->param.amr_octet_aligned;
 | 
			
		||||
	/* Legacy */
 | 
			
		||||
	if (!codec->fmtp[0]
 | 
			
		||||
	    && codec->param_present
 | 
			
		||||
	    && codec->param.amr_octet_aligned_present)
 | 
			
		||||
		return codec->param.amr_octet_aligned;
 | 
			
		||||
 | 
			
		||||
	return osmo_sdp_fmtp_amr_is_octet_aligned(codec->fmtp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Compare two codecs, all parameters must match up */
 | 
			
		||||
@@ -455,19 +473,29 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
	if (conn_dst->end.codecs_assigned)
 | 
			
		||||
		conn_dst->end.codec = &conn_dst->end.codecs[0];
 | 
			
		||||
	else
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (conn_src->end.codecs_assigned)
 | 
			
		||||
		conn_src->end.codec = &conn_src->end.codecs[0];
 | 
			
		||||
	else
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check if the codec has a specific AMR mode (octet-aligned or bandwith-efficient) set. */
 | 
			
		||||
bool mgcp_codec_amr_align_mode_is_indicated(const struct mgcp_rtp_codec *codec)
 | 
			
		||||
{
 | 
			
		||||
	if (codec->param_present == false)
 | 
			
		||||
		return false;
 | 
			
		||||
	if (!codec->param.amr_octet_aligned_present)
 | 
			
		||||
		return false;
 | 
			
		||||
	if (strcmp(codec->subtype_name, "AMR") != 0)
 | 
			
		||||
		return false;
 | 
			
		||||
	return true;
 | 
			
		||||
	if (!codec->fmtp[0]) {
 | 
			
		||||
		/* Legacy */
 | 
			
		||||
		return codec->param_present && codec->param.amr_octet_aligned_present;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Just check for presence, not the actual value. */
 | 
			
		||||
	return osmo_sdp_fmtp_get_val(NULL, 0, codec->fmtp, OSMO_SDP_STR_AMR_OCTET_ALIGN);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Find the payload type number configured for a specific codec by SDP.
 | 
			
		||||
 
 | 
			
		||||
@@ -1189,12 +1189,12 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
 | 
			
		||||
			if (mgcp_conn_rtp_is_iuup(conn_dst) || mgcp_conn_rtp_is_iuup(conn_src)) {
 | 
			
		||||
				/* the iuup code will correctly transform to the correct AMR mode */
 | 
			
		||||
			} else if (mgcp_codec_amr_align_mode_is_indicated(conn_dst->end.codec)) {
 | 
			
		||||
				rc = amr_oa_bwe_convert(endp, msg,
 | 
			
		||||
							conn_dst->end.codec->param.amr_octet_aligned);
 | 
			
		||||
				bool oa = mgcp_codec_amr_is_octet_aligned(conn_dst->end.codec);
 | 
			
		||||
				rc = amr_oa_bwe_convert(endp, msg, oa);
 | 
			
		||||
				if (rc < 0) {
 | 
			
		||||
					LOGPENDP(endp, DRTP, LOGL_ERROR,
 | 
			
		||||
						 "Error in AMR octet-aligned <-> bandwidth-efficient mode conversion (target=%s)\n",
 | 
			
		||||
						 conn_dst->end.codec->param.amr_octet_aligned ? "octet-aligned" : "bandwidth-efficient");
 | 
			
		||||
						 oa ? "octet-aligned" : "bandwidth-efficient");
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			} else if (rtp_end->rfc5993_hr_convert &&
 | 
			
		||||
@@ -1505,16 +1505,18 @@ static int rx_rtp(struct msgb *msg)
 | 
			
		||||
	if (mc->proto == MGCP_PROTO_RTP
 | 
			
		||||
	    && conn_src->end.codec
 | 
			
		||||
	    && mgcp_codec_amr_align_mode_is_indicated(conn_src->end.codec)) {
 | 
			
		||||
		bool src_oa;
 | 
			
		||||
		/* Make sure that the incoming AMR frame format matches the frame format that the call agent has
 | 
			
		||||
		 * communicated via SDP when the connection was created/modfied. */
 | 
			
		||||
		int oa = amr_oa_check((char*)msgb_data(msg), msgb_length(msg));
 | 
			
		||||
		if (oa < 0)
 | 
			
		||||
			return -1;
 | 
			
		||||
		if (((bool)oa) != conn_src->end.codec->param.amr_octet_aligned) {
 | 
			
		||||
		src_oa = mgcp_codec_amr_is_octet_aligned(conn_src->end.codec);
 | 
			
		||||
		if (((bool)oa) != src_oa) {
 | 
			
		||||
			LOG_CONN_RTP(conn_src, LOGL_NOTICE,
 | 
			
		||||
				     "rx_rtp(%u bytes): Expected RTP AMR octet-aligned=%u but got octet-aligned=%u."
 | 
			
		||||
				     " check the config of your call-agent!\n",
 | 
			
		||||
				     msgb_length(msg), conn_src->end.codec->param.amr_octet_aligned, oa);
 | 
			
		||||
				     msgb_length(msg), src_oa, oa);
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -1131,6 +1131,7 @@ mgcp_header_done:
 | 
			
		||||
 | 
			
		||||
	LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
 | 
			
		||||
		 "CRCX: connection successfully created\n");
 | 
			
		||||
 | 
			
		||||
	rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_SUCCESS));
 | 
			
		||||
	mgcp_endp_update(endp);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -35,6 +35,9 @@
 | 
			
		||||
#include <osmocom/mgcp/mgcp_sdp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_protocol.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/sdp/fmtp.h>
 | 
			
		||||
#include <osmocom/sdp/sdp_strings.h>
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
@@ -55,7 +58,7 @@ struct sdp_rtp_map {
 | 
			
		||||
};
 | 
			
		||||
struct sdp_fmtp_param {
 | 
			
		||||
	int payload_type;
 | 
			
		||||
	struct mgcp_codec_param param;
 | 
			
		||||
	const char *fmtp;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -195,11 +198,7 @@ static int fmtp_from_sdp(void *ctx, struct sdp_fmtp_param *fmtp_param, char *sdp
 | 
			
		||||
{
 | 
			
		||||
	char *str;
 | 
			
		||||
	char *str_ptr;
 | 
			
		||||
	char *param_str;
 | 
			
		||||
	unsigned int pt;
 | 
			
		||||
	unsigned int count = 0;
 | 
			
		||||
	char delimiter;
 | 
			
		||||
	unsigned int amr_octet_aligned;
 | 
			
		||||
 | 
			
		||||
	memset(fmtp_param, 0, sizeof(*fmtp_param));
 | 
			
		||||
 | 
			
		||||
@@ -218,40 +217,13 @@ static int fmtp_from_sdp(void *ctx, struct sdp_fmtp_param *fmtp_param, char *sdp
 | 
			
		||||
		goto error;
 | 
			
		||||
	fmtp_param->payload_type = pt;
 | 
			
		||||
 | 
			
		||||
	/* Advance pointer to the beginning of the parameter section and
 | 
			
		||||
	 * tokenize string */
 | 
			
		||||
	/* Advance pointer to the beginning of the parameter section */
 | 
			
		||||
	str_ptr = strstr(str_ptr, " ");
 | 
			
		||||
	if (!str_ptr)
 | 
			
		||||
		goto error;
 | 
			
		||||
	str_ptr++;
 | 
			
		||||
 | 
			
		||||
	param_str = strtok(str_ptr, " ");
 | 
			
		||||
	if (!param_str)
 | 
			
		||||
		goto exit;
 | 
			
		||||
 | 
			
		||||
	while (1) {
 | 
			
		||||
		/* Make sure that we don't get trapped in an endless loop */
 | 
			
		||||
		if (count > 256)
 | 
			
		||||
			goto error;
 | 
			
		||||
 | 
			
		||||
		/* Chop off delimiters ';' at the end */
 | 
			
		||||
		delimiter = str_ptr[strlen(str_ptr) - 1];
 | 
			
		||||
		if (delimiter == ';' || delimiter == ',')
 | 
			
		||||
			str_ptr[strlen(str_ptr) - 1] = '\0';
 | 
			
		||||
 | 
			
		||||
		/* AMR octet aligned parameter (see also RFC 3267, section 8.3) */
 | 
			
		||||
		if (sscanf(param_str, "octet-align=%d", &amr_octet_aligned) == 1) {
 | 
			
		||||
			fmtp_param->param.amr_octet_aligned_present = true;
 | 
			
		||||
			fmtp_param->param.amr_octet_aligned = false;
 | 
			
		||||
			if (amr_octet_aligned == 1)
 | 
			
		||||
				fmtp_param->param.amr_octet_aligned = true;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		param_str = strtok(NULL, " ");
 | 
			
		||||
		if (!param_str)
 | 
			
		||||
			break;
 | 
			
		||||
		count++;
 | 
			
		||||
	}
 | 
			
		||||
	fmtp_param->fmtp = talloc_strdup(ctx, str_ptr);
 | 
			
		||||
 | 
			
		||||
exit:
 | 
			
		||||
	talloc_free(str);
 | 
			
		||||
@@ -299,13 +271,13 @@ static int audio_ip_from_sdp(struct osmo_sockaddr *dst_addr, char *sdp)
 | 
			
		||||
 | 
			
		||||
/* Pick optional fmtp parameters by payload type, if there are no fmtp
 | 
			
		||||
 * parameters, a nullpointer is returned */
 | 
			
		||||
static struct mgcp_codec_param *param_by_pt(int pt, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len)
 | 
			
		||||
static const char *param_by_pt(int pt, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < fmtp_params_len; i++) {
 | 
			
		||||
		if (fmtp_params[i].payload_type == pt)
 | 
			
		||||
			return &fmtp_params[i].param;
 | 
			
		||||
			return fmtp_params[i].fmtp;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
@@ -326,7 +298,6 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
 | 
			
		||||
	unsigned int codecs_used = 0;
 | 
			
		||||
	struct sdp_fmtp_param fmtp_params[MGCP_MAX_CODECS];
 | 
			
		||||
	unsigned int fmtp_used = 0;
 | 
			
		||||
	struct mgcp_codec_param *codec_param;
 | 
			
		||||
	char ipbuf[INET6_ADDRSTRLEN];
 | 
			
		||||
	char *line;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
@@ -421,8 +392,8 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
 | 
			
		||||
 | 
			
		||||
	/* Store parsed codec information */
 | 
			
		||||
	for (i = 0; i < codecs_used; i++) {
 | 
			
		||||
		codec_param = param_by_pt(codecs[i].payload_type, fmtp_params, fmtp_used);
 | 
			
		||||
		rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line, codec_param);
 | 
			
		||||
		const char *fmtp = param_by_pt(codecs[i].payload_type, fmtp_params, fmtp_used);
 | 
			
		||||
		rc = mgcp_codec_add2(conn, codecs[i].payload_type, codecs[i].map_line, fmtp);
 | 
			
		||||
		if (rc < 0)
 | 
			
		||||
			LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "failed to add codec\n");
 | 
			
		||||
	}
 | 
			
		||||
@@ -436,10 +407,12 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
 | 
			
		||||
	if (codecs_used == 0)
 | 
			
		||||
		LOGPC(DLMGCP, LOGL_NOTICE, "none");
 | 
			
		||||
	for (i = 0; i < codecs_used; i++) {
 | 
			
		||||
		LOGPC(DLMGCP, LOGL_NOTICE, "%d=%s",
 | 
			
		||||
		LOGPC(DLMGCP, LOGL_NOTICE, " %d=%s%s%s%s",
 | 
			
		||||
		      rtp->codecs[i].payload_type,
 | 
			
		||||
		      strlen(rtp->codecs[i].subtype_name) ? rtp->codecs[i].subtype_name : "unknown");
 | 
			
		||||
		LOGPC(DLMGCP, LOGL_NOTICE, " ");
 | 
			
		||||
		      strlen(rtp->codecs[i].subtype_name) ? rtp->codecs[i].subtype_name : "unknown",
 | 
			
		||||
		      rtp->codecs[i].fmtp[0] ? ",fmtp='" : "",
 | 
			
		||||
		      rtp->codecs[i].fmtp,
 | 
			
		||||
		      rtp->codecs[i].fmtp[0] ? "'" : "");
 | 
			
		||||
	}
 | 
			
		||||
	LOGPC(DLMGCP, LOGL_NOTICE, "\n");
 | 
			
		||||
 | 
			
		||||
@@ -494,18 +467,15 @@ static int add_fmtp(struct msgb *sdp, struct sdp_fmtp_param *fmtp_params, unsign
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < fmtp_params_len; i++) {
 | 
			
		||||
		bool first = true;
 | 
			
		||||
		rc = msgb_printf(sdp, "a=fmtp:%u", fmtp_params[i].payload_type);
 | 
			
		||||
		if (rc < 0)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
 | 
			
		||||
		/* Add amr octet align parameter */
 | 
			
		||||
		if (fmtp_params[i].param.amr_octet_aligned_present) {
 | 
			
		||||
			if (fmtp_params[i].param.amr_octet_aligned)
 | 
			
		||||
				rc = msgb_printf(sdp, " octet-align=1");
 | 
			
		||||
			else
 | 
			
		||||
				rc = msgb_printf(sdp, " octet-align=0");
 | 
			
		||||
			if (rc < 0)
 | 
			
		||||
				return -EINVAL;
 | 
			
		||||
		if (fmtp_params[i].fmtp) {
 | 
			
		||||
			msgb_printf(sdp, "%s%s", first ? " " : ";", fmtp_params[i].fmtp);
 | 
			
		||||
			first = false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		rc = msgb_printf(sdp, "\r\n");
 | 
			
		||||
@@ -529,7 +499,6 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
 | 
			
		||||
	const struct mgcp_rtp_codec *codec;
 | 
			
		||||
	const char *audio_name;
 | 
			
		||||
	int payload_type;
 | 
			
		||||
	struct sdp_fmtp_param fmtp_param;
 | 
			
		||||
	int rc;
 | 
			
		||||
	int payload_types[1];
 | 
			
		||||
	int local_port;
 | 
			
		||||
@@ -578,12 +547,22 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
 | 
			
		||||
				goto buffer_too_small;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (codec->param_present) {
 | 
			
		||||
			fmtp_param.payload_type = payload_type;
 | 
			
		||||
			fmtp_param.param = codec->param;
 | 
			
		||||
			fmtp_params[0] = fmtp_param;
 | 
			
		||||
		if (codec->fmtp[0]) {
 | 
			
		||||
			fmtp_params[0] = (struct sdp_fmtp_param){
 | 
			
		||||
				.payload_type = payload_type,
 | 
			
		||||
				.fmtp = codec->fmtp,
 | 
			
		||||
			};
 | 
			
		||||
			fmtp_params_len = 1;
 | 
			
		||||
		} else if (codec->param_present) {
 | 
			
		||||
			/* Legacy */
 | 
			
		||||
			fmtp_params[0] = (struct sdp_fmtp_param){
 | 
			
		||||
				.payload_type = payload_type,
 | 
			
		||||
			};
 | 
			
		||||
			fmtp_params_len = 1;
 | 
			
		||||
			fmtp_params[0].fmtp = (codec->param.amr_octet_aligned ?
 | 
			
		||||
					       OSMO_SDP_STR_AMR_OCTET_ALIGN_1 : OSMO_SDP_STR_AMR_OCTET_ALIGN_0);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		rc = add_fmtp(sdp, fmtp_params, fmtp_params_len);
 | 
			
		||||
		if (rc < 0)
 | 
			
		||||
			goto buffer_too_small;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										24
									
								
								src/libosmo-sdp/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/libosmo-sdp/Makefile.am
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
AM_CPPFLAGS = \
 | 
			
		||||
	$(all_includes) \
 | 
			
		||||
	-I$(top_srcdir)/include \
 | 
			
		||||
	-I$(top_builddir) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_CFLAGS = \
 | 
			
		||||
	-Wall \
 | 
			
		||||
	$(LIBOSMOCORE_CFLAGS) \
 | 
			
		||||
	$(TALLOC_CFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
noinst_LTLIBRARIES = \
 | 
			
		||||
	libosmo-sdp.la \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
libosmo_sdp_la_SOURCES = \
 | 
			
		||||
	fmtp.c \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
libosmo_sdp_la_LIBADD = \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(TALLOC_LIBS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
							
								
								
									
										150
									
								
								src/libosmo-sdp/fmtp.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								src/libosmo-sdp/fmtp.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,150 @@
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2023-2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Neels Hofmeyr
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/sdp/sdp_strings.h>
 | 
			
		||||
#include <osmocom/sdp/fmtp.h>
 | 
			
		||||
 | 
			
		||||
/* End of current fmtp parameter. Return a pointer to the next ';' character, if present, or the terminating '\0'. */
 | 
			
		||||
static const char *osmo_sdp_fmtp_end(const char *fmtp)
 | 
			
		||||
{
 | 
			
		||||
	if (!fmtp)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	for (; *fmtp && *fmtp != ';'; fmtp++);
 | 
			
		||||
	return fmtp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Start of next fmtp parameter. Return a pointer to the first character of the next fmtp parameter's name, or the
 | 
			
		||||
 * terminating '\0'. */
 | 
			
		||||
static const char *osmo_sdp_fmtp_next(const char *fmtp)
 | 
			
		||||
{
 | 
			
		||||
	if (!fmtp)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	fmtp = osmo_sdp_fmtp_end(fmtp);
 | 
			
		||||
	for (; *fmtp && (*fmtp == ';' || isspace(*fmtp)); fmtp++);
 | 
			
		||||
	return fmtp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Parse a given SDP fmtp value string, returning the value of a specific option, if present.
 | 
			
		||||
 *
 | 
			
		||||
 * Example:
 | 
			
		||||
 *
 | 
			
		||||
 *   const char *fmtp_vals = "octet-align=1;mode-set=0,2,4,7";
 | 
			
		||||
 *
 | 
			
		||||
 *   char mode_set_str[23];
 | 
			
		||||
 *   if (osmo_sdp_fmtp_get_val(mode_set_str, sizeof(mode_set_str), fmtp_vals, "mode-set")) {
 | 
			
		||||
 *           // option 'mode-set' is present, now mode_set_str == "0,2,4,7"
 | 
			
		||||
 *           use_modeset(mode_set_str);
 | 
			
		||||
 *   } else {
 | 
			
		||||
 *           // if 'mode-set' were not present...
 | 
			
		||||
 *           use_modeset(MY_DEFAULT_MODESET);
 | 
			
		||||
 *   }
 | 
			
		||||
 *
 | 
			
		||||
 * \param[out] val  Buffer to write the option's value to.
 | 
			
		||||
 * \param[in] val_size  Space available in val.
 | 
			
		||||
 * \param[in] fmtp  fmtp value string to parse -- must not contain the "a=fmtp:N " prefix, only the value part.
 | 
			
		||||
 * \param[in] option_name  Which fmtp option to get the value for.
 | 
			
		||||
 * \return true when the option was found, false when it was not present.
 | 
			
		||||
 */
 | 
			
		||||
bool osmo_sdp_fmtp_get_val(char *val, size_t val_size, const char *fmtp, const char *option_name)
 | 
			
		||||
{
 | 
			
		||||
	const char *pos = fmtp;
 | 
			
		||||
	const char *end;
 | 
			
		||||
	int option_name_len = strlen(option_name);
 | 
			
		||||
	for (; pos && *pos; pos = osmo_sdp_fmtp_next(pos)) {
 | 
			
		||||
		if (!osmo_str_startswith(pos, option_name))
 | 
			
		||||
			continue;
 | 
			
		||||
		pos += option_name_len;
 | 
			
		||||
		if (*pos != '=')
 | 
			
		||||
			continue;
 | 
			
		||||
		pos++;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!pos || !*pos)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	end = osmo_sdp_fmtp_end(pos);
 | 
			
		||||
	OSMO_ASSERT(end);
 | 
			
		||||
	if (val && val_size)
 | 
			
		||||
		osmo_strlcpy(val, pos, OSMO_MIN(val_size, end - pos + 1));
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Parse a given SDP fmtp value string, returning the value of a specific integer option, if present.
 | 
			
		||||
 *
 | 
			
		||||
 * Example:
 | 
			
		||||
 *
 | 
			
		||||
 *   const char *fmtp_vals = "octet-align=1;mode-set=0,2,4,7";
 | 
			
		||||
 *   bool oa = osmo_sdp_fmtp_get_int(fmtp_vals, OSMO_SDP_AMR_OCTET_ALIGN_NAME, 1);
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in] fmtp  fmtp value string to parse -- must not contain the "a=fmtp:N " prefix, only the value part.
 | 
			
		||||
 * \param[in] option_name  Which fmtp option to get the value for.
 | 
			
		||||
 * \param[in] default_value  If option_name is not present or cannot be parsed as integer, return this instead.
 | 
			
		||||
 * \return the integer value when the option was found and actually an integer, default_value otherwise.
 | 
			
		||||
 */
 | 
			
		||||
int64_t osmo_sdp_fmtp_get_int(const char *fmtp, const char *option_name, int64_t default_value)
 | 
			
		||||
{
 | 
			
		||||
	char val[128];
 | 
			
		||||
	if (!osmo_sdp_fmtp_get_val(val, sizeof(val), fmtp, option_name))
 | 
			
		||||
		return default_value;
 | 
			
		||||
	if (!val[0])
 | 
			
		||||
		return default_value;
 | 
			
		||||
	int64_t i;
 | 
			
		||||
	if (osmo_str_to_int64(&i, val, 10, INT64_MIN, INT64_MAX)) {
 | 
			
		||||
		/* error parsing number */
 | 
			
		||||
		return default_value;
 | 
			
		||||
	}
 | 
			
		||||
	return i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Return true if octet-align is present and set to 1 in the given AMR related fmtp value.
 | 
			
		||||
 * Default to octet-align=0, i.e. bandwidth-efficient mode.
 | 
			
		||||
 *
 | 
			
		||||
 * See RFC4867 "RTP Payload Format for AMR and AMR-WB" sections "8.1. AMR Media Type Registration" and "8.2. AMR-WB
 | 
			
		||||
 * Media Type Registration":
 | 
			
		||||
 *
 | 
			
		||||
 *    octet-align: Permissible values are 0 and 1.  If 1, octet-align
 | 
			
		||||
 *                 operation SHALL be used.  If 0 or if not present,
 | 
			
		||||
 *                 bandwidth-efficient operation is employed.
 | 
			
		||||
 *
 | 
			
		||||
 * https://tools.ietf.org/html/rfc4867
 | 
			
		||||
 */
 | 
			
		||||
bool osmo_sdp_fmtp_amr_is_octet_aligned(const char *fmtp)
 | 
			
		||||
{
 | 
			
		||||
	return osmo_sdp_fmtp_get_int(fmtp, OSMO_SDP_STR_AMR_OCTET_ALIGN, 0) == 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void strip_whitespace(char *str)
 | 
			
		||||
{
 | 
			
		||||
	char *i = str;
 | 
			
		||||
	char *o = str;
 | 
			
		||||
	for (; *i; i++, o++) {
 | 
			
		||||
		while (isspace(*i))
 | 
			
		||||
			i++;
 | 
			
		||||
		*o = *i;
 | 
			
		||||
		if (!*i)
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -25,12 +25,6 @@ osmo_mgw_SOURCES = \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
osmo_mgw_LDADD = \
 | 
			
		||||
	$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.la \
 | 
			
		||||
	$(LIBOSMOCTRL_LIBS) \
 | 
			
		||||
	$(LIBOSMONETIF_LIBS) \
 | 
			
		||||
	$(LIBOSMOABIS_LIBS) \
 | 
			
		||||
	$(LIBOSMOTRAU_LIBS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
SUBDIRS = \
 | 
			
		||||
	mgcp_client \
 | 
			
		||||
	mgcp \
 | 
			
		||||
	sdp \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
 | 
			
		||||
 
 | 
			
		||||
@@ -34,7 +34,7 @@ mgcp_test_SOURCES = \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
mgcp_test_LDADD = \
 | 
			
		||||
	$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
 | 
			
		||||
	$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.la \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
 
 | 
			
		||||
@@ -550,7 +550,7 @@ static void test_strline(void)
 | 
			
		||||
	"t=0 0\r\n" \
 | 
			
		||||
	"m=audio 16012 RTP/AVP 111\r\n" \
 | 
			
		||||
	"a=rtpmap:111 AMR/8000/1\r\n" \
 | 
			
		||||
	"a=fmtp:111 octet-align=1\r\n" \
 | 
			
		||||
	"a=fmtp:111 mode-change-capability=2; octet-align=1\r\n" \
 | 
			
		||||
	"a=ptime:20\r\n"
 | 
			
		||||
 | 
			
		||||
#define CRCX_NO_LCO_NO_SDP_RET \
 | 
			
		||||
@@ -838,6 +838,7 @@ static void test_messages(void)
 | 
			
		||||
			}
 | 
			
		||||
		} else if (check_response(msg->data, t->exp_resp) != 0) {
 | 
			
		||||
			printf("%s failed.\n", t->name);
 | 
			
		||||
			fflush(stdout);
 | 
			
		||||
			OSMO_ASSERT(false);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										38
									
								
								tests/sdp/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								tests/sdp/Makefile.am
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
AM_CPPFLAGS = \
 | 
			
		||||
	$(all_includes) \
 | 
			
		||||
	-I$(top_srcdir)/include \
 | 
			
		||||
	-I$(top_builddir)/include \
 | 
			
		||||
	-I$(top_srcdir) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_CFLAGS = \
 | 
			
		||||
	-Wall \
 | 
			
		||||
	-ggdb3 \
 | 
			
		||||
	$(LIBOSMOCORE_CFLAGS) \
 | 
			
		||||
	$(COVERAGE_CFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_LDFLAGS = \
 | 
			
		||||
	$(COVERAGE_LDFLAGS) \
 | 
			
		||||
	-no-install \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST = \
 | 
			
		||||
	sdp_fmtp_test.ok \
 | 
			
		||||
	sdp_fmtp_test.err \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
check_PROGRAMS = \
 | 
			
		||||
	sdp_fmtp_test \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
sdp_fmtp_test_SOURCES = \
 | 
			
		||||
	sdp_fmtp_test.c \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
sdp_fmtp_test_LDADD = \
 | 
			
		||||
	$(top_builddir)/src/libosmo-sdp/libosmo-sdp.la \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
update_exp:
 | 
			
		||||
	$(builddir)/sdp_fmtp_test >$(srcdir)/sdp_fmtp_test.ok 2>$(srcdir)/sdp_fmtp_test.err
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
@@ -13,3 +13,10 @@ 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
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user