add libosmo-sdp: add sdp_msg.h,.c
Change-Id: Id53c2c6eea3726f63a1399ae985f8aa3344e32c8
This commit is contained in:
parent
948e66b259
commit
24c09fbacb
|
@ -11,6 +11,7 @@ nobase_include_HEADERS = \
|
||||||
osmocom/sdp/fmtp.h \
|
osmocom/sdp/fmtp.h \
|
||||||
osmocom/sdp/sdp_codec.h \
|
osmocom/sdp/sdp_codec.h \
|
||||||
osmocom/sdp/sdp_codec_list.h \
|
osmocom/sdp/sdp_codec_list.h \
|
||||||
|
osmocom/sdp/sdp_msg.h \
|
||||||
osmocom/sdp/sdp_strings.h \
|
osmocom/sdp/sdp_strings.h \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
/* Public API for SDP message encoding and decoding */
|
||||||
|
/*
|
||||||
|
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
|
* All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0+
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <osmocom/core/sockaddr_str.h>
|
||||||
|
|
||||||
|
#include <osmocom/sdp/sdp_codec.h>
|
||||||
|
#include <osmocom/sdp/sdp_codec_list.h>
|
||||||
|
|
||||||
|
/* Media Direction Attributes "a=recvonly", "a=sendrecv", "a=sendonly", "a=inactive" RFC-8866 6.7. */
|
||||||
|
enum osmo_sdp_media_direcion_e {
|
||||||
|
OSMO_SDP_MDIR_UNSET = 0,
|
||||||
|
OSMO_SDP_MDIR_RECVONLY = 1,
|
||||||
|
OSMO_SDP_MDIR_SENDRECV = 2,
|
||||||
|
OSMO_SDP_MDIR_SENDONLY = 3,
|
||||||
|
OSMO_SDP_MDIR_INACTIVE = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Session Description Protocol (SDP) message, RFC-8866. */
|
||||||
|
struct osmo_sdp_msg {
|
||||||
|
/* 5.2 Origin ("o="). */
|
||||||
|
struct {
|
||||||
|
struct osmo_sockaddr_str addr;
|
||||||
|
char *username;
|
||||||
|
int64_t sess_id;
|
||||||
|
int64_t sess_version;
|
||||||
|
} origin;
|
||||||
|
|
||||||
|
/* 5.3 Session Name ("s="). */
|
||||||
|
char *session_name;
|
||||||
|
|
||||||
|
/* 5.7 Connection Information ("c=") and port from 5.14 Media Descriptions ("m="). */
|
||||||
|
struct osmo_sockaddr_str rtp;
|
||||||
|
|
||||||
|
/* 5.9. Time Active ("t="). */
|
||||||
|
struct {
|
||||||
|
int64_t start;
|
||||||
|
int64_t stop;
|
||||||
|
} time_active;
|
||||||
|
|
||||||
|
/* 6.4 "a=ptime:<val>". */
|
||||||
|
unsigned int ptime;
|
||||||
|
|
||||||
|
/* 6.7 "a=sendrecv"... */
|
||||||
|
enum osmo_sdp_media_direcion_e media_direction;
|
||||||
|
|
||||||
|
/* List of codecs defined in the SDP message.
|
||||||
|
* This should not be NULL -- osmo_sdp_msg_alloc() returns an empty osmo_sdp_codec_list instance, ready for
|
||||||
|
* adding codecs.
|
||||||
|
* Combination of:
|
||||||
|
* - payload_type numbers from 5.14 Media Descriptions ("m="),
|
||||||
|
* - 6.6 "a=rtpmap",
|
||||||
|
* - 6.15 Format Parameters "a=fmtp".
|
||||||
|
*/
|
||||||
|
struct osmo_sdp_codec_list *codecs;
|
||||||
|
|
||||||
|
/* For future extension, always set to false. */
|
||||||
|
bool v2;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct osmo_sdp_err {
|
||||||
|
int rc;
|
||||||
|
/* Point at the position that caused the error, in the src string. */
|
||||||
|
const char *src_str;
|
||||||
|
/* Nr of characters at *src_str that are relevant to the error. */
|
||||||
|
size_t src_str_len;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct osmo_sdp_msg *osmo_sdp_msg_alloc(void *ctx);
|
||||||
|
|
||||||
|
struct osmo_sdp_msg *osmo_sdp_msg_decode(void *ctx, const char *src, struct osmo_sdp_err *err);
|
||||||
|
|
||||||
|
int osmo_sdp_msg_encode_buf(char *dst, size_t dst_size, const struct osmo_sdp_msg *sdp);
|
||||||
|
char *osmo_sdp_msg_encode_c(void *ctx, const struct osmo_sdp_msg *sdp);
|
||||||
|
|
||||||
|
int osmo_sdp_msg_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_msg *sdp, bool summarize);
|
||||||
|
char *osmo_sdp_msg_to_str_c(void *ctx, const struct osmo_sdp_msg *sdp, bool summarize);
|
|
@ -17,6 +17,7 @@ noinst_LTLIBRARIES = \
|
||||||
libosmo_sdp_la_SOURCES = \
|
libosmo_sdp_la_SOURCES = \
|
||||||
sdp_codec.c \
|
sdp_codec.c \
|
||||||
sdp_codec_list.c \
|
sdp_codec_list.c \
|
||||||
|
sdp_msg.c \
|
||||||
sdp_internal.c \
|
sdp_internal.c \
|
||||||
fmtp.c \
|
fmtp.c \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
|
@ -0,0 +1,448 @@
|
||||||
|
/* Implementation for SDP message encoding and decoding */
|
||||||
|
/*
|
||||||
|
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
|
* All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0+
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include <osmocom/core/utils.h>
|
||||||
|
|
||||||
|
#include <osmocom/sdp/sdp_msg.h>
|
||||||
|
#include <osmocom/sdp/sdp_strings.h>
|
||||||
|
#include <osmocom/sdp/sdp_internal.h>
|
||||||
|
|
||||||
|
static const char * const mdir_str[] = {
|
||||||
|
[OSMO_SDP_MDIR_UNSET] = "-",
|
||||||
|
[OSMO_SDP_MDIR_SENDONLY] = OSMO_SDP_STR_SENDONLY,
|
||||||
|
[OSMO_SDP_MDIR_RECVONLY] = OSMO_SDP_STR_RECVONLY,
|
||||||
|
[OSMO_SDP_MDIR_SENDRECV] = OSMO_SDP_STR_SENDRECV,
|
||||||
|
[OSMO_SDP_MDIR_INACTIVE] = OSMO_SDP_STR_INACTIVE,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*! Convert struct osmo_sdp_msg to the actual SDP protocol representation. */
|
||||||
|
int osmo_sdp_msg_encode_buf(char *dst, size_t dst_size, const struct osmo_sdp_msg *sdp)
|
||||||
|
{
|
||||||
|
const struct osmo_sdp_codec *codec;
|
||||||
|
struct osmo_strbuf sb = { .buf = dst, .len = dst_size };
|
||||||
|
const char *oip;
|
||||||
|
char oipv;
|
||||||
|
const char *ip;
|
||||||
|
char ipv;
|
||||||
|
|
||||||
|
if (!sdp) {
|
||||||
|
OSMO_STRBUF_PRINTF(sb, "%s", "");
|
||||||
|
return sb.chars_needed;
|
||||||
|
}
|
||||||
|
|
||||||
|
oip = sdp->origin.addr.ip[0] ? sdp->origin.addr.ip : "0.0.0.0";
|
||||||
|
oipv = (osmo_ip_str_type(oip) == AF_INET6) ? '6' : '4';
|
||||||
|
|
||||||
|
ip = sdp->rtp.ip[0] ? sdp->rtp.ip : "0.0.0.0";
|
||||||
|
ipv = (osmo_ip_str_type(oip) == AF_INET6) ? '6' : '4';
|
||||||
|
|
||||||
|
OSMO_STRBUF_PRINTF(sb,
|
||||||
|
"v=0\r\n"
|
||||||
|
"o=%s %"PRId64" %"PRId64" 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, sdp->origin.sess_version,
|
||||||
|
oipv, oip,
|
||||||
|
sdp->session_name ? : "-",
|
||||||
|
ipv, ip,
|
||||||
|
sdp->time_active.start,
|
||||||
|
sdp->time_active.stop,
|
||||||
|
sdp->rtp.port);
|
||||||
|
|
||||||
|
/* Append all payload type numbers to 'm=audio <port> RTP/AVP 3 4 112' line */
|
||||||
|
osmo_sdp_codec_list_foreach(codec, sdp->codecs)
|
||||||
|
OSMO_STRBUF_PRINTF(sb, " %d", codec->payload_type);
|
||||||
|
OSMO_STRBUF_PRINTF(sb, "\r\n");
|
||||||
|
|
||||||
|
/* Add details for all codecs */
|
||||||
|
osmo_sdp_codec_list_foreach(codec, sdp->codecs) {
|
||||||
|
if (!osmo_sdp_codec_is_set(codec))
|
||||||
|
continue;
|
||||||
|
OSMO_STRBUF_PRINTF(sb, OSMO_SDP_A_PREFIX(OSMO_SDP_STR_RTPMAP) "%d %s/%d\r\n", codec->payload_type, codec->encoding_name,
|
||||||
|
codec->rate > 0 ? codec->rate : 8000);
|
||||||
|
if (codec->fmtp && codec->fmtp[0])
|
||||||
|
OSMO_STRBUF_PRINTF(sb, OSMO_SDP_A_PREFIX(OSMO_SDP_STR_FMTP) "%d %s\r\n", codec->payload_type, codec->fmtp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sdp->ptime)
|
||||||
|
OSMO_STRBUF_PRINTF(sb, OSMO_SDP_A_PREFIX(OSMO_SDP_STR_PTIME) "%d\r\n", sdp->ptime);
|
||||||
|
|
||||||
|
if (sdp->media_direction != OSMO_SDP_MDIR_UNSET && sdp->media_direction < ARRAY_SIZE(mdir_str))
|
||||||
|
OSMO_STRBUF_PRINTF(sb, "a=%s\r\n", mdir_str[sdp->media_direction]);
|
||||||
|
|
||||||
|
return sb.chars_needed;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *osmo_sdp_msg_encode_c(void *ctx, const struct osmo_sdp_msg *sdp)
|
||||||
|
{
|
||||||
|
OSMO_NAME_C_IMPL(ctx, 256, "osmo_sdp_msg_to_str_c-ERROR", osmo_sdp_msg_encode_buf, sdp)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return the first line ending (or the end of the string) at or after the given string position. */
|
||||||
|
const char *get_line_end(const char *src)
|
||||||
|
{
|
||||||
|
const char *line_end = strchr(src, '\r');
|
||||||
|
if (!line_end)
|
||||||
|
line_end = strchr(src, '\n');
|
||||||
|
if (!line_end)
|
||||||
|
line_end = src + strlen(src);
|
||||||
|
return line_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool str_is_attrib(const char *str, const char *attrib_name, char expect_next_char)
|
||||||
|
{
|
||||||
|
char next_c;
|
||||||
|
if (!osmo_str_startswith(str, attrib_name))
|
||||||
|
return false;
|
||||||
|
next_c = str[strlen(attrib_name)];
|
||||||
|
if (expect_next_char == next_c)
|
||||||
|
return true;
|
||||||
|
/* Treat \0 as equivalent with line end */
|
||||||
|
if (!expect_next_char && (next_c == '\r' || next_c == '\n'))
|
||||||
|
return true;
|
||||||
|
/* It started with the string, but continued otherwise */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum osmo_sdp_media_direcion_e check_for_media_direction(const char *str)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < ARRAY_SIZE(mdir_str); i++) {
|
||||||
|
if (i == OSMO_SDP_MDIR_UNSET)
|
||||||
|
continue;
|
||||||
|
if (str_is_attrib(str, mdir_str[i], 0))
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return OSMO_SDP_MDIR_UNSET;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct osmo_sdp_codec *find_or_create_payload_type(struct osmo_sdp_msg *sdp, unsigned int payload_type)
|
||||||
|
{
|
||||||
|
struct osmo_sdp_codec *codec;
|
||||||
|
codec = osmo_sdp_codec_list_by_payload_type(sdp->codecs, payload_type);
|
||||||
|
if (!codec) {
|
||||||
|
codec = osmo_sdp_codec_list_add_empty(sdp->codecs);
|
||||||
|
codec->payload_type = payload_type;
|
||||||
|
codec->rate = 8000;
|
||||||
|
}
|
||||||
|
return codec;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* parse a line like 'a=rtpmap:0 PCMU/8000', 'a=fmtp:112 octet-align=1; mode-set=4', 'a=ptime:20'.
|
||||||
|
* The src should point at the character after 'a=', e.g. at the start of 'rtpmap', 'fmtp', 'ptime'
|
||||||
|
*/
|
||||||
|
int sdp_parse_attrib(struct osmo_sdp_msg *sdp, const char *src)
|
||||||
|
{
|
||||||
|
unsigned int payload_type;
|
||||||
|
struct osmo_sdp_codec *codec;
|
||||||
|
enum osmo_sdp_media_direcion_e mdir;
|
||||||
|
const char *line_end = get_line_end(src);
|
||||||
|
|
||||||
|
if (str_is_attrib(src, OSMO_SDP_STR_RTPMAP, ':')) {
|
||||||
|
/* "a=rtpmap:96 AMR/8000" */
|
||||||
|
struct token audio_name;
|
||||||
|
unsigned int channels = 1;
|
||||||
|
if (sscanf(src, OSMO_SDP_STR_RTPMAP ":%u", &payload_type) != 1)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
codec = find_or_create_payload_type(sdp, payload_type);
|
||||||
|
|
||||||
|
audio_name.start = strchr(src, ' ');
|
||||||
|
if (!audio_name.start)
|
||||||
|
return -EINVAL;
|
||||||
|
audio_name.start++;
|
||||||
|
if (audio_name.start >= get_line_end(src))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
audio_name.end = strchr(audio_name.start, '/');
|
||||||
|
if (!audio_name.end || audio_name.end > line_end)
|
||||||
|
audio_name.end = line_end;
|
||||||
|
token_copy(codec, &codec->encoding_name, &audio_name);
|
||||||
|
/* '< 1' is enough, the second '/%u' is optional */
|
||||||
|
if (sscanf(audio_name.end, "/%u/%u", &codec->rate, &channels) < 1)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (channels != 1)
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (str_is_attrib(src, OSMO_SDP_STR_FMTP, ':')) {
|
||||||
|
/* "a=fmtp:112 octet-align=1;mode-set=0,1,2,3" */
|
||||||
|
struct token fmtp_str;
|
||||||
|
const char *line_end = get_line_end(src);
|
||||||
|
if (sscanf(src, OSMO_SDP_STR_FMTP ":%u", &payload_type) != 1)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
codec = find_or_create_payload_type(sdp, payload_type);
|
||||||
|
|
||||||
|
fmtp_str.start = strchr(src, ' ');
|
||||||
|
if (!fmtp_str.start)
|
||||||
|
return -EINVAL;
|
||||||
|
fmtp_str.start++;
|
||||||
|
if (fmtp_str.start >= line_end)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
fmtp_str.end = line_end;
|
||||||
|
token_copy(codec, &codec->fmtp, &fmtp_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (str_is_attrib(src, OSMO_SDP_STR_PTIME, ':')) {
|
||||||
|
/* "a=ptime:20" */
|
||||||
|
if (sscanf(src, OSMO_SDP_STR_PTIME ":%u", &sdp->ptime) != 1)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* "a=sendrecv" ... */
|
||||||
|
else if ((mdir = check_for_media_direction(src)) != OSMO_SDP_MDIR_UNSET) {
|
||||||
|
sdp->media_direction = mdir;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct value_string fixed_payload_types[] = {
|
||||||
|
{ 0, "PCMU" },
|
||||||
|
{ 3, "GSM" },
|
||||||
|
{ 8, "PCMA" },
|
||||||
|
{ 18, "G729" },
|
||||||
|
{ 110, "GSM-EFR" },
|
||||||
|
{ 111, "GSM-HR-08" },
|
||||||
|
{ 112, "AMR" },
|
||||||
|
{ 113, "AMR-WB" },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Parse a line like 'm=audio 16398 RTP/AVP 0 3 8 96 112', starting after the '=' */
|
||||||
|
static int sdp_parse_media_description(struct osmo_sdp_msg *sdp, const char *src)
|
||||||
|
{
|
||||||
|
unsigned int port;
|
||||||
|
int i;
|
||||||
|
const char *payload_type_str;
|
||||||
|
const char *line_end = get_line_end(src);
|
||||||
|
if (sscanf(src, "audio %u RTP/AVP", &port) < 1)
|
||||||
|
return -ENOTSUP;
|
||||||
|
|
||||||
|
if (port > 0xffff)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
sdp->rtp.port = port;
|
||||||
|
|
||||||
|
/* skip "audio 12345 RTP/AVP ", i.e. 3 spaces on */
|
||||||
|
payload_type_str = src;
|
||||||
|
for (i = 0; i < 3; i++) {
|
||||||
|
payload_type_str = strchr(payload_type_str, ' ');
|
||||||
|
if (!payload_type_str)
|
||||||
|
return -EINVAL;
|
||||||
|
while (*payload_type_str == ' ')
|
||||||
|
payload_type_str++;
|
||||||
|
if (payload_type_str >= line_end)
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse listing of payload type numbers after "RTP/AVP" */
|
||||||
|
while (payload_type_str < line_end) {
|
||||||
|
unsigned int payload_type;
|
||||||
|
struct osmo_sdp_codec *codec;
|
||||||
|
const char *encoding_name;
|
||||||
|
if (sscanf(payload_type_str, "%u", &payload_type) < 1)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
codec = find_or_create_payload_type(sdp, payload_type);
|
||||||
|
|
||||||
|
/* Fill in encoding name for fixed payload types */
|
||||||
|
encoding_name = get_value_string_or_null(fixed_payload_types, codec->payload_type);
|
||||||
|
if (encoding_name)
|
||||||
|
osmo_talloc_replace_string(codec, &codec->encoding_name, encoding_name);
|
||||||
|
|
||||||
|
payload_type_str = strchr(payload_type_str, ' ');
|
||||||
|
if (!payload_type_str)
|
||||||
|
payload_type_str = line_end;
|
||||||
|
while (*payload_type_str == ' ')
|
||||||
|
payload_type_str++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* parse a line like 'c=IN IP4 192.168.11.151' starting after the '=' */
|
||||||
|
static int sdp_parse_connection_info(struct osmo_sdp_msg *sdp, const char *src)
|
||||||
|
{
|
||||||
|
char ipv[10];
|
||||||
|
char addr_str[INET6_ADDRSTRLEN];
|
||||||
|
if (sscanf(src, "IN %s %s", ipv, addr_str) < 2)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (strcmp(ipv, "IP4") && strcmp(ipv, "IP6"))
|
||||||
|
return -ENOTSUP;
|
||||||
|
|
||||||
|
return osmo_sockaddr_str_from_str(&sdp->rtp, addr_str, sdp->rtp.port);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* parse a line like 'o=jdoe 3724394400 3724394405 IN IP4 198.51.100.1' starting after the '=' */
|
||||||
|
static int sdp_parse_origin(struct osmo_sdp_msg *sdp, const char *src)
|
||||||
|
{
|
||||||
|
struct token username;
|
||||||
|
char ipv[16] = {};
|
||||||
|
char addr_str[INET6_ADDRSTRLEN] = {};
|
||||||
|
const char *line_end = get_line_end(src);
|
||||||
|
|
||||||
|
username.start = src;
|
||||||
|
username.end = strchr(src, ' ');
|
||||||
|
if (!username.end || username.end >= line_end)
|
||||||
|
return -EINVAL;
|
||||||
|
token_copy(sdp, &sdp->origin.username, &username);
|
||||||
|
|
||||||
|
if (sscanf(username.end + 1, "%"PRId64" %"PRId64" IN %15s %45s",
|
||||||
|
&sdp->origin.sess_id, &sdp->origin.sess_version, ipv, addr_str) < 4)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (strcmp(ipv, "IP4") && strcmp(ipv, "IP6"))
|
||||||
|
return -ENOTSUP;
|
||||||
|
return osmo_sockaddr_str_from_str(&sdp->origin.addr, addr_str, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdp_parse_session_name(struct osmo_sdp_msg *sdp, const char *src)
|
||||||
|
{
|
||||||
|
const char *line_end = get_line_end(src);
|
||||||
|
if (sdp->session_name)
|
||||||
|
talloc_free(sdp->session_name);
|
||||||
|
if (line_end <= src)
|
||||||
|
sdp->session_name = NULL;
|
||||||
|
else
|
||||||
|
sdp->session_name = talloc_strndup(sdp, src, line_end - src);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct osmo_sdp_msg *osmo_sdp_msg_alloc(void *ctx)
|
||||||
|
{
|
||||||
|
struct osmo_sdp_msg *sdp;
|
||||||
|
sdp = talloc_zero(ctx, struct osmo_sdp_msg);
|
||||||
|
sdp->codecs = osmo_sdp_codec_list_alloc(sdp);
|
||||||
|
return sdp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse SDP string into struct osmo_sdp_msg. Return 0 on success, negative on error.
|
||||||
|
* Return a new osmo_sdp_msg instance allocated from ctx, or NULL on error.
|
||||||
|
* When NULL is returned and if err is non-NULL, details of the error are returned in err->*.
|
||||||
|
*/
|
||||||
|
struct osmo_sdp_msg *osmo_sdp_msg_decode(void *ctx, const char *src, struct osmo_sdp_err *err)
|
||||||
|
{
|
||||||
|
struct osmo_sdp_msg *sdp;
|
||||||
|
const char *pos;
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
*err = (struct osmo_sdp_err){};
|
||||||
|
|
||||||
|
sdp = osmo_sdp_msg_alloc(ctx);
|
||||||
|
|
||||||
|
for (pos = src; pos && *pos; pos++) {
|
||||||
|
char attrib;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
if (*pos == '\r' || *pos == '\n')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Expecting only lines starting with 'X='. Not being too strict about it is probably alright. */
|
||||||
|
if (pos[1] != '=')
|
||||||
|
goto next_line;
|
||||||
|
|
||||||
|
attrib = *pos;
|
||||||
|
pos += 2;
|
||||||
|
switch (attrib) {
|
||||||
|
/* a=... */
|
||||||
|
case 'a':
|
||||||
|
rc = sdp_parse_attrib(sdp, pos);
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
rc = sdp_parse_media_description(sdp, pos);
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
rc = sdp_parse_connection_info(sdp, pos);
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
rc = sdp_parse_origin(sdp, pos);
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
rc = sdp_parse_session_name(sdp, pos);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* ignore any other parameters */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc) {
|
||||||
|
if (err) {
|
||||||
|
const char *line_end = get_line_end(pos);
|
||||||
|
/* shift back to include the 'x=' part as well */
|
||||||
|
pos -= 2;
|
||||||
|
*err = (struct osmo_sdp_err){
|
||||||
|
.rc = rc,
|
||||||
|
.src_str = pos,
|
||||||
|
.src_str_len = line_end - pos,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
talloc_free(sdp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
next_line:
|
||||||
|
pos = strstr(pos, "\r\n");
|
||||||
|
if (!pos)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sdp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Short single-line representation of an SDP message, convenient for logging.
|
||||||
|
* To obtain a valid SDP message, use osmo_sdp_msg_encode_buf() instead.
|
||||||
|
*/
|
||||||
|
int osmo_sdp_msg_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_msg *sdp, bool summarize)
|
||||||
|
{
|
||||||
|
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||||
|
if (!sdp) {
|
||||||
|
OSMO_STRBUF_PRINTF(sb, "NULL");
|
||||||
|
return sb.chars_needed;
|
||||||
|
}
|
||||||
|
|
||||||
|
OSMO_STRBUF_PRINTF(sb, OSMO_SOCKADDR_STR_FMT, OSMO_SOCKADDR_STR_FMT_ARGS(&sdp->rtp));
|
||||||
|
OSMO_STRBUF_PRINTF(sb, "{");
|
||||||
|
OSMO_STRBUF_APPEND(sb, osmo_sdp_codec_list_to_str_buf, sdp->codecs, summarize);
|
||||||
|
OSMO_STRBUF_PRINTF(sb, "}");
|
||||||
|
return sb.chars_needed;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *osmo_sdp_msg_to_str_c(void *ctx, const struct osmo_sdp_msg *sdp, bool summarize)
|
||||||
|
{
|
||||||
|
OSMO_NAME_C_IMPL(ctx, 128, "sdp_msg_to_str_c-ERROR", osmo_sdp_msg_to_str_buf, sdp, summarize)
|
||||||
|
}
|
|
@ -27,6 +27,7 @@ EXTRA_DIST = \
|
||||||
check_PROGRAMS = \
|
check_PROGRAMS = \
|
||||||
sdp_fmtp_test \
|
sdp_fmtp_test \
|
||||||
sdp_codec_test \
|
sdp_codec_test \
|
||||||
|
sdp_msg_test \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
sdp_fmtp_test_SOURCES = \
|
sdp_fmtp_test_SOURCES = \
|
||||||
|
@ -46,6 +47,16 @@ sdp_codec_test_LDADD = \
|
||||||
$(LIBOSMOCORE_LIBS) \
|
$(LIBOSMOCORE_LIBS) \
|
||||||
$(NULL)
|
$(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:
|
update_exp:
|
||||||
$(builddir)/sdp_fmtp_test >$(srcdir)/sdp_fmtp_test.ok 2>$(srcdir)/sdp_fmtp_test.err
|
$(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_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
|
||||||
|
|
|
@ -0,0 +1,754 @@
|
||||||
|
#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__);
|
||||||
|
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;
|
||||||
|
|
||||||
|
printf("\n[%d]\n", i);
|
||||||
|
dump_sdp(t->sdp_input, "sdp input: ");
|
||||||
|
|
||||||
|
sdp_msg = osmo_sdp_msg_decode(ctx, t->sdp_input, NULL);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct intersect_test_data {
|
||||||
|
const char *descr;
|
||||||
|
const char *sdp_msg_a;
|
||||||
|
const char *sdp_msg_b;
|
||||||
|
const char *expect_intersection;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define SDP_1 \
|
||||||
|
"v=0\r\n" \
|
||||||
|
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n" \
|
||||||
|
"s=GSM Call\r\n" \
|
||||||
|
"c=IN IP4 23.42.23.42\r\n" \
|
||||||
|
"t=0 0\r\n" \
|
||||||
|
"m=audio 30436 RTP/AVP 112 3 111 110\r\n" \
|
||||||
|
"a=rtpmap:112 AMR/8000\r\n" \
|
||||||
|
"a=fmtp:112 octet-align=1\r\n" \
|
||||||
|
"a=rtpmap:3 GSM/8000\r\n" \
|
||||||
|
"a=rtpmap:111 GSM-HR-08/8000\r\n" \
|
||||||
|
"a=rtpmap:110 GSM-EFR/8000\r\n" \
|
||||||
|
"a=ptime:20\r\n"
|
||||||
|
|
||||||
|
#define SDP_2 \
|
||||||
|
"v=0\r\n" \
|
||||||
|
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n" \
|
||||||
|
"s=GSM Call\r\n" \
|
||||||
|
"c=IN IP4 23.42.23.42\r\n" \
|
||||||
|
"t=0 0\r\n" \
|
||||||
|
"m=audio 30436 RTP/AVP 112 110\r\n" \
|
||||||
|
"a=rtpmap:112 AMR/8000\r\n" \
|
||||||
|
"a=fmtp:112 octet-align=1\r\n" \
|
||||||
|
"a=rtpmap:110 GSM-EFR/8000\r\n" \
|
||||||
|
"a=ptime:20\r\n"
|
||||||
|
|
||||||
|
#define SDP_3 \
|
||||||
|
"v=0\r\n" \
|
||||||
|
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n" \
|
||||||
|
"s=GSM Call\r\n" \
|
||||||
|
"c=IN IP4 23.42.23.42\r\n" \
|
||||||
|
"t=0 0\r\n" \
|
||||||
|
"m=audio 30436 RTP/AVP 3 111 123\r\n" \
|
||||||
|
"a=rtpmap:3 GSM/8000\r\n" \
|
||||||
|
"a=rtpmap:111 GSM-HR-08/8000\r\n" \
|
||||||
|
"a=rtpmap:123 FOO/8000\r\n" \
|
||||||
|
"a=ptime:20\r\n"
|
||||||
|
|
||||||
|
#define SDP_4 \
|
||||||
|
"v=0\r\n" \
|
||||||
|
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n" \
|
||||||
|
"s=GSM Call\r\n" \
|
||||||
|
"c=IN IP4 23.42.23.42\r\n" \
|
||||||
|
"t=0 0\r\n" \
|
||||||
|
"m=audio 30436 RTP/AVP 3 111\r\n" \
|
||||||
|
"a=rtpmap:3 GSM/8000\r\n" \
|
||||||
|
"a=rtpmap:111 GSM-HR-08/8000\r\n" \
|
||||||
|
"a=ptime:20\r\n"
|
||||||
|
|
||||||
|
#define SDP_5 \
|
||||||
|
"v=0\r\n" \
|
||||||
|
"o=libosmo-sdp 0 0 IN IP4 0.0.0.0\r\n" \
|
||||||
|
"s=GSM Call\r\n" \
|
||||||
|
"c=IN IP4 0.0.0.0\r\n" \
|
||||||
|
"t=0 0\r\n" \
|
||||||
|
"m=audio 0 RTP/AVP 112 113 110 3 111\r\n" \
|
||||||
|
"a=rtpmap:112 AMR/8000\r\n" \
|
||||||
|
"a=fmtp:112 octet-align=1;mode-set=0,1,2,3\r\n" \
|
||||||
|
"a=rtpmap:113 AMR-WB/8000\r\n" \
|
||||||
|
"a=fmtp:113 octet-align=1\r\n" \
|
||||||
|
"a=rtpmap:110 GSM-EFR/8000\r\n" \
|
||||||
|
"a=rtpmap:3 GSM/8000\r\n" \
|
||||||
|
"a=rtpmap:111 GSM-HR-08/8000\r\n" \
|
||||||
|
"a=ptime:20\r\n"
|
||||||
|
|
||||||
|
static const struct intersect_test_data intersect_tests[] = {
|
||||||
|
{
|
||||||
|
"identical codecs lead to no change"
|
||||||
|
,
|
||||||
|
SDP_1
|
||||||
|
,
|
||||||
|
"c=IN IP4 5.6.7.8\r\n" \
|
||||||
|
"m=audio 12345 RTP/AVP 112 3 111 110\r\n"
|
||||||
|
"a=rtpmap:112 AMR/8000\r\n"
|
||||||
|
"a=fmtp:112 octet-align=1\r\n"
|
||||||
|
"a=rtpmap:3 GSM/8000\r\n"
|
||||||
|
"a=rtpmap:111 GSM-HR-08/8000\r\n"
|
||||||
|
"a=rtpmap:110 GSM-EFR/8000\r\n"
|
||||||
|
,
|
||||||
|
SDP_1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identical codecs in different order also lead to no change"
|
||||||
|
,
|
||||||
|
SDP_1
|
||||||
|
,
|
||||||
|
"c=IN IP4 5.6.7.8\r\n" \
|
||||||
|
"m=audio 12345 RTP/AVP 3 110 111 112\r\n"
|
||||||
|
"a=rtpmap:3 GSM/8000\r\n"
|
||||||
|
"a=rtpmap:110 GSM-EFR/8000\r\n"
|
||||||
|
"a=rtpmap:111 GSM-HR-08/8000\r\n"
|
||||||
|
"a=rtpmap:112 AMR/8000\r\n"
|
||||||
|
"a=fmtp:112 octet-align=1\r\n"
|
||||||
|
,
|
||||||
|
SDP_1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identical codecs with mismatching payload type numbers also lead to no change"
|
||||||
|
,
|
||||||
|
SDP_1
|
||||||
|
,
|
||||||
|
"c=IN IP4 5.6.7.8\r\n" \
|
||||||
|
"m=audio 12345 RTP/AVP 96 97 98 99\r\n"
|
||||||
|
"a=rtpmap:96 GSM/8000\r\n"
|
||||||
|
"a=rtpmap:97 GSM-EFR/8000\r\n"
|
||||||
|
"a=rtpmap:98 GSM-HR-08/8000\r\n"
|
||||||
|
"a=rtpmap:99 AMR/8000\r\n"
|
||||||
|
"a=fmtp:99 octet-align=1\r\n"
|
||||||
|
,
|
||||||
|
SDP_1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identical codecs plus some extra codecs also lead to no change"
|
||||||
|
,
|
||||||
|
SDP_1
|
||||||
|
,
|
||||||
|
"c=IN IP4 5.6.7.8\r\n" \
|
||||||
|
"m=audio 12345 RTP/AVP 8 0 96 97 98 99\r\n"
|
||||||
|
"a=rtpmap:8 PCMA/8000\r\n"
|
||||||
|
"a=rtpmap:0 PCMU/8000\r\n"
|
||||||
|
"a=rtpmap:96 GSM/8000\r\n"
|
||||||
|
"a=rtpmap:97 GSM-EFR/8000\r\n"
|
||||||
|
"a=rtpmap:98 GSM-HR-08/8000\r\n"
|
||||||
|
"a=rtpmap:99 AMR/8000\r\n"
|
||||||
|
"a=fmtp:99 octet-align=1\r\n"
|
||||||
|
,
|
||||||
|
SDP_1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"some codecs removed",
|
||||||
|
SDP_1,
|
||||||
|
SDP_2,
|
||||||
|
SDP_2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"other codecs removed",
|
||||||
|
SDP_1,
|
||||||
|
SDP_3,
|
||||||
|
SDP_4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"all codecs removed",
|
||||||
|
SDP_1
|
||||||
|
,
|
||||||
|
"s=empty"
|
||||||
|
,
|
||||||
|
"v=0\r\n" \
|
||||||
|
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n" \
|
||||||
|
"s=GSM Call\r\n" \
|
||||||
|
"c=IN IP4 23.42.23.42\r\n" \
|
||||||
|
"t=0 0\r\n" \
|
||||||
|
"m=audio 30436 RTP/AVP\r\n" \
|
||||||
|
"a=ptime:20\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"some real world test case",
|
||||||
|
SDP_5, SDP_5, SDP_5,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *sdp_msg_logstr(const struct osmo_sdp_msg *sdp_msg)
|
||||||
|
{
|
||||||
|
static char buf[1024];
|
||||||
|
osmo_sdp_msg_encode_buf(buf, sizeof(buf), sdp_msg);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_intersect(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
bool ok = true;
|
||||||
|
void *ctx = talloc_named_const(test_ctx, 0, __func__);
|
||||||
|
|
||||||
|
printf("\n\n%s\n", __func__);
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(intersect_tests); i++) {
|
||||||
|
const struct intersect_test_data *t = &intersect_tests[i];
|
||||||
|
struct osmo_sdp_msg *sdp_msg_a = NULL;
|
||||||
|
struct osmo_sdp_msg *sdp_msg_b = NULL;
|
||||||
|
char str[1024];
|
||||||
|
printf("\n[%d] %s\n", i, t->descr);
|
||||||
|
dump_sdp(t->sdp_msg_a, "SDP A: ");
|
||||||
|
dump_sdp(t->sdp_msg_b, " SDP B: ");
|
||||||
|
|
||||||
|
sdp_msg_a = osmo_sdp_msg_decode(ctx, t->sdp_msg_a, NULL);
|
||||||
|
if (!sdp_msg_a) {
|
||||||
|
printf("ERROR parsing SDP A\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dump_sdp(sdp_msg_logstr(sdp_msg_a), "parsed SDP A: ");
|
||||||
|
|
||||||
|
sdp_msg_b = osmo_sdp_msg_decode(ctx, t->sdp_msg_b, NULL);
|
||||||
|
if (!sdp_msg_b) {
|
||||||
|
printf("ERROR parsing SDP B\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dump_sdp(sdp_msg_logstr(sdp_msg_b), "parsed SDP B: ");
|
||||||
|
|
||||||
|
osmo_sdp_codec_list_intersection(sdp_msg_a->codecs, sdp_msg_b->codecs,
|
||||||
|
&osmo_sdp_codec_cmp_equivalent,
|
||||||
|
false);
|
||||||
|
osmo_sdp_msg_encode_buf(str, sizeof(str), sdp_msg_a);
|
||||||
|
dump_sdp(str, "intersection(a,b): ");
|
||||||
|
if (strcmp(str, t->expect_intersection)) {
|
||||||
|
int j;
|
||||||
|
ok = false;
|
||||||
|
printf("ERROR:\n");
|
||||||
|
dump_sdp(t->expect_intersection, "expect_intersection: ");
|
||||||
|
for (j = 0; t->expect_intersection[j]; j++) {
|
||||||
|
if (t->expect_intersection[j] != str[j]) {
|
||||||
|
printf("ERROR at position %d, at:\n", j);
|
||||||
|
dump_sdp(str + j, " mismatch: ");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
printf("[%d] ok\n", i);
|
||||||
|
|
||||||
|
report(ctx);
|
||||||
|
printf("talloc_free(sdp_msg_a)\n");
|
||||||
|
talloc_free(sdp_msg_a);
|
||||||
|
report(ctx);
|
||||||
|
printf("talloc_free(sdp_msg_b)\n");
|
||||||
|
talloc_free(sdp_msg_b);
|
||||||
|
report(ctx);
|
||||||
|
|
||||||
|
if (talloc_total_blocks(ctx) != 1) {
|
||||||
|
printf("ERROR: memleak\n");
|
||||||
|
talloc_free_children(ctx);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
OSMO_ASSERT(ok);
|
||||||
|
talloc_free(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sdp_select_test_data {
|
||||||
|
const char *sdp;
|
||||||
|
const struct osmo_sdp_codec_cmp_flags *cmpf;
|
||||||
|
const struct osmo_sdp_codec select;
|
||||||
|
const char *expect_sdp;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct osmo_sdp_codec_cmp_flags pt_only = { .payload_type = true };
|
||||||
|
|
||||||
|
static const struct sdp_select_test_data sdp_select_tests[] = {
|
||||||
|
{
|
||||||
|
"v=0\r\n"
|
||||||
|
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
|
||||||
|
"s=GSM Call\r\n"
|
||||||
|
"c=IN IP4 23.42.23.42\r\n"
|
||||||
|
"t=0 0\r\n"
|
||||||
|
"m=audio 30436 RTP/AVP 112 3 111 110\r\n"
|
||||||
|
"a=rtpmap:112 AMR/8000\r\n"
|
||||||
|
"a=fmtp:112 octet-align=1\r\n"
|
||||||
|
"a=rtpmap:3 GSM/8000\r\n"
|
||||||
|
"a=rtpmap:111 GSM-HR-08/8000\r\n"
|
||||||
|
"a=rtpmap:110 GSM-EFR/8000\r\n"
|
||||||
|
"a=ptime:20\r\n"
|
||||||
|
,
|
||||||
|
&pt_only,
|
||||||
|
{ .payload_type = 112, },
|
||||||
|
NULL
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"v=0\r\n"
|
||||||
|
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
|
||||||
|
"s=GSM Call\r\n"
|
||||||
|
"c=IN IP4 23.42.23.42\r\n"
|
||||||
|
"t=0 0\r\n"
|
||||||
|
"m=audio 30436 RTP/AVP 112 3 111 110\r\n"
|
||||||
|
"a=rtpmap:112 AMR/8000\r\n"
|
||||||
|
"a=fmtp:112 octet-align=1\r\n"
|
||||||
|
"a=rtpmap:3 GSM/8000\r\n"
|
||||||
|
"a=rtpmap:111 GSM-HR-08/8000\r\n"
|
||||||
|
"a=rtpmap:110 GSM-EFR/8000\r\n"
|
||||||
|
"a=ptime:20\r\n"
|
||||||
|
,
|
||||||
|
&pt_only,
|
||||||
|
{ .payload_type = 3, },
|
||||||
|
"v=0\r\n"
|
||||||
|
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
|
||||||
|
"s=GSM Call\r\n"
|
||||||
|
"c=IN IP4 23.42.23.42\r\n"
|
||||||
|
"t=0 0\r\n"
|
||||||
|
"m=audio 30436 RTP/AVP 3 112 111 110\r\n"
|
||||||
|
"a=rtpmap:3 GSM/8000\r\n"
|
||||||
|
"a=rtpmap:112 AMR/8000\r\n"
|
||||||
|
"a=fmtp:112 octet-align=1\r\n"
|
||||||
|
"a=rtpmap:111 GSM-HR-08/8000\r\n"
|
||||||
|
"a=rtpmap:110 GSM-EFR/8000\r\n"
|
||||||
|
"a=ptime:20\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"v=0\r\n"
|
||||||
|
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
|
||||||
|
"s=GSM Call\r\n"
|
||||||
|
"c=IN IP4 23.42.23.42\r\n"
|
||||||
|
"t=0 0\r\n"
|
||||||
|
"m=audio 30436 RTP/AVP 112 3 111 110\r\n"
|
||||||
|
"a=rtpmap:112 AMR/8000\r\n"
|
||||||
|
"a=fmtp:112 octet-align=1\r\n"
|
||||||
|
"a=rtpmap:3 GSM/8000\r\n"
|
||||||
|
"a=rtpmap:111 GSM-HR-08/8000\r\n"
|
||||||
|
"a=rtpmap:110 GSM-EFR/8000\r\n"
|
||||||
|
"a=ptime:20\r\n"
|
||||||
|
,
|
||||||
|
&pt_only,
|
||||||
|
{ .payload_type = 111, },
|
||||||
|
"v=0\r\n"
|
||||||
|
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
|
||||||
|
"s=GSM Call\r\n"
|
||||||
|
"c=IN IP4 23.42.23.42\r\n"
|
||||||
|
"t=0 0\r\n"
|
||||||
|
"m=audio 30436 RTP/AVP 111 112 3 110\r\n"
|
||||||
|
"a=rtpmap:111 GSM-HR-08/8000\r\n"
|
||||||
|
"a=rtpmap:112 AMR/8000\r\n"
|
||||||
|
"a=fmtp:112 octet-align=1\r\n"
|
||||||
|
"a=rtpmap:3 GSM/8000\r\n"
|
||||||
|
"a=rtpmap:110 GSM-EFR/8000\r\n"
|
||||||
|
"a=ptime:20\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"v=0\r\n"
|
||||||
|
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
|
||||||
|
"s=GSM Call\r\n"
|
||||||
|
"c=IN IP4 23.42.23.42\r\n"
|
||||||
|
"t=0 0\r\n"
|
||||||
|
"m=audio 30436 RTP/AVP 112 3 111 110\r\n"
|
||||||
|
"a=rtpmap:112 AMR/8000\r\n"
|
||||||
|
"a=fmtp:112 octet-align=1\r\n"
|
||||||
|
"a=rtpmap:3 GSM/8000\r\n"
|
||||||
|
"a=rtpmap:111 GSM-HR-08/8000\r\n"
|
||||||
|
"a=rtpmap:110 GSM-EFR/8000\r\n"
|
||||||
|
"a=ptime:20\r\n"
|
||||||
|
,
|
||||||
|
&pt_only,
|
||||||
|
{ .payload_type = 110, },
|
||||||
|
"v=0\r\n"
|
||||||
|
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
|
||||||
|
"s=GSM Call\r\n"
|
||||||
|
"c=IN IP4 23.42.23.42\r\n"
|
||||||
|
"t=0 0\r\n"
|
||||||
|
"m=audio 30436 RTP/AVP 110 112 3 111\r\n"
|
||||||
|
"a=rtpmap:110 GSM-EFR/8000\r\n"
|
||||||
|
"a=rtpmap:112 AMR/8000\r\n"
|
||||||
|
"a=fmtp:112 octet-align=1\r\n"
|
||||||
|
"a=rtpmap:3 GSM/8000\r\n"
|
||||||
|
"a=rtpmap:111 GSM-HR-08/8000\r\n"
|
||||||
|
"a=ptime:20\r\n"
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
static void test_select(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
bool ok = true;
|
||||||
|
void *ctx = talloc_named_const(test_ctx, 0, __func__);
|
||||||
|
void *print_ctx = talloc_named_const(test_ctx, 0, "print");
|
||||||
|
|
||||||
|
printf("\n\n%s\n", __func__);
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(sdp_select_tests); i++) {
|
||||||
|
const struct sdp_select_test_data *t = &sdp_select_tests[i];
|
||||||
|
struct osmo_sdp_msg *sdp_msg;
|
||||||
|
char buf[1024];
|
||||||
|
const char *expect_sdp;
|
||||||
|
|
||||||
|
printf("\n[%d]\n", i);
|
||||||
|
sdp_msg = osmo_sdp_msg_decode(ctx, t->sdp, NULL);
|
||||||
|
if (!sdp_msg) {
|
||||||
|
printf("ERROR parsing SDP\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
printf("SDP: %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, sdp_msg->codecs, false));
|
||||||
|
|
||||||
|
printf("Select: %s\n", osmo_sdp_codec_to_str_c(print_ctx, &t->select));
|
||||||
|
osmo_sdp_codec_list_move_to_first(sdp_msg->codecs, &t->select, t->cmpf);
|
||||||
|
|
||||||
|
printf("SDP: %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, sdp_msg->codecs, false));
|
||||||
|
osmo_sdp_msg_encode_buf(buf, sizeof(buf), sdp_msg);
|
||||||
|
|
||||||
|
expect_sdp = t->expect_sdp ? : t->sdp;
|
||||||
|
if (strcmp(buf, expect_sdp)) {
|
||||||
|
int j;
|
||||||
|
ok = false;
|
||||||
|
printf("ERROR:\n");
|
||||||
|
dump_sdp(buf, "selection result: ");
|
||||||
|
dump_sdp(expect_sdp, "expect result: ");
|
||||||
|
for (j = 0; expect_sdp[j]; j++) {
|
||||||
|
if (expect_sdp[j] != buf[j]) {
|
||||||
|
printf("ERROR at position %d, at:\n", j);
|
||||||
|
dump_sdp(buf + j, " mismatch: ");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
printf("[%d] ok\n", i);
|
||||||
|
|
||||||
|
report(ctx);
|
||||||
|
printf("talloc_free(sdp_msg)\n");
|
||||||
|
talloc_free(sdp_msg);
|
||||||
|
report(ctx);
|
||||||
|
if (talloc_total_blocks(ctx) != 1) {
|
||||||
|
printf("ERROR: memleak\n");
|
||||||
|
talloc_free_children(ctx);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
talloc_free_children(print_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
OSMO_ASSERT(ok);
|
||||||
|
talloc_free(ctx);
|
||||||
|
talloc_free(print_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct my_obj {
|
||||||
|
struct osmo_sdp_msg *sdp_msg;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct my_obj *my_obj_alloc(void *ctx)
|
||||||
|
{
|
||||||
|
struct my_obj *o = talloc_zero(ctx, struct my_obj);
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_obj_members(void)
|
||||||
|
{
|
||||||
|
void *ctx = talloc_named_const(test_ctx, 0, __func__);
|
||||||
|
void *print_ctx = talloc_named_const(test_ctx, 0, "print");
|
||||||
|
int i;
|
||||||
|
|
||||||
|
struct my_obj *o;
|
||||||
|
|
||||||
|
printf("\n\n--- %s()\n", __func__);
|
||||||
|
o = my_obj_alloc(ctx);
|
||||||
|
|
||||||
|
o->sdp_msg = osmo_sdp_msg_alloc(o);
|
||||||
|
|
||||||
|
printf("o->sdp_msg = '%s'\n", osmo_sdp_msg_encode_c(print_ctx, o->sdp_msg));
|
||||||
|
report(ctx);
|
||||||
|
|
||||||
|
const struct osmo_sdp_codec all_codecs[] = {
|
||||||
|
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
|
||||||
|
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
|
||||||
|
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
|
||||||
|
};
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(all_codecs); i++)
|
||||||
|
osmo_sdp_codec_list_add(o->sdp_msg->codecs, &all_codecs[i], false, false);
|
||||||
|
|
||||||
|
printf("o->sdp_msg = '%s'\n", osmo_sdp_msg_encode_c(print_ctx, o->sdp_msg));
|
||||||
|
|
||||||
|
report(ctx);
|
||||||
|
printf("talloc_free(o)\n");
|
||||||
|
talloc_free(o);
|
||||||
|
report(ctx);
|
||||||
|
talloc_free(ctx);
|
||||||
|
talloc_free(print_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef void (*test_func_t)(void);
|
||||||
|
|
||||||
|
static const test_func_t test_func[] = {
|
||||||
|
test_parse_and_compose,
|
||||||
|
test_intersect,
|
||||||
|
test_select,
|
||||||
|
test_obj_members,
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
test_ctx = talloc_named_const(NULL, 0, "sdp_codec_test");
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(test_func); i++) {
|
||||||
|
|
||||||
|
test_func[i]();
|
||||||
|
|
||||||
|
if (talloc_total_blocks(test_ctx) != 1) {
|
||||||
|
talloc_report_full(test_ctx, stderr);
|
||||||
|
printf("ERROR after test %d: memory leak\n", i);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
talloc_free(test_ctx);
|
||||||
|
return 0;
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -27,3 +27,10 @@ cat $abs_srcdir/sdp/sdp_codec_test.ok > expout
|
||||||
cat $abs_srcdir/sdp/sdp_codec_test.err > experr
|
cat $abs_srcdir/sdp/sdp_codec_test.err > experr
|
||||||
AT_CHECK([$abs_top_builddir/tests/sdp/sdp_codec_test], [], [expout], [experr])
|
AT_CHECK([$abs_top_builddir/tests/sdp/sdp_codec_test], [], [expout], [experr])
|
||||||
AT_CLEANUP
|
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
|
||||||
|
|
Loading…
Reference in New Issue