mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-11-03 20:38:59 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			531 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			531 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Asterisk -- An open source telephony toolkit.
 | 
						|
 *
 | 
						|
 * Copyright (C) 2014, Digium, Inc.
 | 
						|
 *
 | 
						|
 * Joshua Colp <jcolp@digium.com>
 | 
						|
 *
 | 
						|
 * See http://www.asterisk.org for more information about
 | 
						|
 * the Asterisk project. Please do not directly contact
 | 
						|
 * any of the maintainers of this project for assistance;
 | 
						|
 * the project provides a web site, mailing lists and IRC
 | 
						|
 * channels for your use.
 | 
						|
 *
 | 
						|
 * This program is free software, distributed under the terms of
 | 
						|
 * the GNU General Public License Version 2. See the LICENSE file
 | 
						|
 * at the top of the source tree.
 | 
						|
 */
 | 
						|
 | 
						|
/*! \file
 | 
						|
 *
 | 
						|
 * \brief Media Format Bitfield Compatibility API
 | 
						|
 *
 | 
						|
 * \author Joshua Colp <jcolp@digium.com>
 | 
						|
 */
 | 
						|
 | 
						|
/*** MODULEINFO
 | 
						|
	<support_level>core</support_level>
 | 
						|
 ***/
 | 
						|
 | 
						|
#include "asterisk.h"
 | 
						|
 | 
						|
#include "asterisk/logger.h"
 | 
						|
#include "asterisk/astobj2.h"
 | 
						|
#include "asterisk/codec.h"
 | 
						|
#include "asterisk/format.h"
 | 
						|
#include "asterisk/format_compatibility.h"
 | 
						|
#include "asterisk/format_cache.h"
 | 
						|
#include "asterisk/format_cap.h"
 | 
						|
#include "asterisk/utils.h"
 | 
						|
 | 
						|
#include "include/codec_pref.h"
 | 
						|
#include "include/format_compatibility.h"
 | 
						|
 | 
						|
void iax2_codec_pref_convert(struct iax2_codec_pref *pref, char *buf, size_t size, int right)
 | 
						|
{
 | 
						|
	static int differential = (int) 'A';
 | 
						|
	int x;
 | 
						|
 | 
						|
	if (right) {
 | 
						|
		--size;/* Save room for the nul string terminator. */
 | 
						|
		for (x = 0; x < ARRAY_LEN(pref->order) && x < size; ++x) {
 | 
						|
			if (!pref->order[x]) {
 | 
						|
				break;
 | 
						|
			}
 | 
						|
 | 
						|
			buf[x] = pref->order[x] + differential;
 | 
						|
		}
 | 
						|
 | 
						|
		buf[x] = '\0';
 | 
						|
	} else {
 | 
						|
		for (x = 0; x < ARRAY_LEN(pref->order) && x < size; ++x) {
 | 
						|
			if (buf[x] == '\0') {
 | 
						|
				break;
 | 
						|
			}
 | 
						|
 | 
						|
			pref->order[x] = buf[x] - differential;
 | 
						|
			pref->framing[x] = 0;
 | 
						|
		}
 | 
						|
 | 
						|
		if (x < ARRAY_LEN(pref->order)) {
 | 
						|
			pref->order[x] = 0;
 | 
						|
			pref->framing[x] = 0;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
struct ast_format *iax2_codec_pref_index(struct iax2_codec_pref *pref, int idx, struct ast_format **result)
 | 
						|
{
 | 
						|
	if (0 <= idx && idx < ARRAY_LEN(pref->order) && pref->order[idx]) {
 | 
						|
		uint64_t pref_bitfield;
 | 
						|
 | 
						|
		pref_bitfield = iax2_codec_pref_order_value_to_format_bitfield(pref->order[idx]);
 | 
						|
		*result = ast_format_compatibility_bitfield2format(pref_bitfield);
 | 
						|
	} else {
 | 
						|
		*result = NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	return *result;
 | 
						|
}
 | 
						|
 | 
						|
int iax2_codec_pref_to_cap(struct iax2_codec_pref *pref, struct ast_format_cap *cap)
 | 
						|
{
 | 
						|
	int idx;
 | 
						|
 | 
						|
	for (idx = 0; idx < ARRAY_LEN(pref->order); ++idx) {
 | 
						|
		uint64_t pref_bitfield;
 | 
						|
		struct ast_format *pref_format;
 | 
						|
 | 
						|
		pref_bitfield = iax2_codec_pref_order_value_to_format_bitfield(pref->order[idx]);
 | 
						|
		if (!pref_bitfield) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		pref_format = ast_format_compatibility_bitfield2format(pref_bitfield);
 | 
						|
		if (pref_format && ast_format_cap_append(cap, pref_format, pref->framing[idx])) {
 | 
						|
			return -1;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
int iax2_codec_pref_best_bitfield2cap(uint64_t bitfield, struct iax2_codec_pref *prefs, struct ast_format_cap *cap)
 | 
						|
{
 | 
						|
	uint64_t best_bitfield;
 | 
						|
	struct ast_format *format;
 | 
						|
 | 
						|
	/* Add any user preferred codecs first. */
 | 
						|
	if (prefs) {
 | 
						|
		int idx;
 | 
						|
 | 
						|
		for (idx = 0; bitfield && idx < ARRAY_LEN(prefs->order); ++idx) {
 | 
						|
			best_bitfield = iax2_codec_pref_order_value_to_format_bitfield(prefs->order[idx]);
 | 
						|
			if (!best_bitfield) {
 | 
						|
				break;
 | 
						|
			}
 | 
						|
 | 
						|
			if (best_bitfield & bitfield) {
 | 
						|
				format = ast_format_compatibility_bitfield2format(best_bitfield);
 | 
						|
				if (format && ast_format_cap_append(cap, format, prefs->framing[idx])) {
 | 
						|
					return -1;
 | 
						|
				}
 | 
						|
 | 
						|
				/* Remove just added codec. */
 | 
						|
				bitfield &= ~best_bitfield;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* Add the hard coded "best" codecs. */
 | 
						|
	while (bitfield) {
 | 
						|
		best_bitfield = iax2_format_compatibility_best(bitfield);
 | 
						|
		if (!best_bitfield) {
 | 
						|
			/* No more codecs considered best. */
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		format = ast_format_compatibility_bitfield2format(best_bitfield);
 | 
						|
		/* The best_bitfield should always be convertible to a format. */
 | 
						|
		ast_assert(format != NULL);
 | 
						|
 | 
						|
		if (ast_format_cap_append(cap, format, 0)) {
 | 
						|
			return -1;
 | 
						|
		}
 | 
						|
 | 
						|
		/* Remove just added "best" codec to find the next "best". */
 | 
						|
		bitfield &= ~best_bitfield;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Add any remaining codecs. */
 | 
						|
	if (bitfield) {
 | 
						|
		int bit;
 | 
						|
 | 
						|
		for (bit = 0; bit < 64; ++bit) {
 | 
						|
			uint64_t mask = (1ULL << bit);
 | 
						|
 | 
						|
			if (mask & bitfield) {
 | 
						|
				format = ast_format_compatibility_bitfield2format(mask);
 | 
						|
				if (format && ast_format_cap_append(cap, format, 0)) {
 | 
						|
					return -1;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
int iax2_codec_pref_string(struct iax2_codec_pref *pref, char *buf, size_t size)
 | 
						|
{
 | 
						|
	int x;
 | 
						|
	struct ast_format_cap *cap;
 | 
						|
	size_t total_len;
 | 
						|
	char *cur;
 | 
						|
 | 
						|
	/* This function is useless if you have less than a 6 character buffer.
 | 
						|
	 * '(...)' is six characters. */
 | 
						|
	if (size < 6) {
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Convert the preferences into a format cap so that we can read the format names */
 | 
						|
	cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
 | 
						|
	if (!cap || iax2_codec_pref_to_cap(pref, cap)) {
 | 
						|
		strcpy(buf, "(...)"); /* Safe */
 | 
						|
		ao2_cleanup(cap);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	/* We know that at a minimum, 3 characters are used - (, ), and \0 */
 | 
						|
	total_len = size - 3;
 | 
						|
 | 
						|
	/* This character has already been accounted for total_len purposes */
 | 
						|
	buf[0] = '(';
 | 
						|
	cur = buf + 1;
 | 
						|
 | 
						|
	/* Loop through the formats and write as many into the buffer as we can */
 | 
						|
	for (x = 0; x < ast_format_cap_count(cap); x++) {
 | 
						|
		size_t name_len;
 | 
						|
		struct ast_format *fmt = ast_format_cap_get_format(cap, x);
 | 
						|
		const char *name = ast_format_get_name(fmt);
 | 
						|
 | 
						|
		name_len = strlen(name);
 | 
						|
 | 
						|
		/* all entries after the first need a delimiter character */
 | 
						|
		if (x) {
 | 
						|
			name_len++;
 | 
						|
		}
 | 
						|
 | 
						|
		/* Terminate the list early if we don't have room for the entry.
 | 
						|
		 * If it's not the last entry in the list, save enough room to write '...'.
 | 
						|
		 */
 | 
						|
		if (((x == ast_format_cap_count(cap) - 1) && (total_len < name_len)) ||
 | 
						|
				((x < ast_format_cap_count(cap) - 1) && (total_len < name_len + 3))) {
 | 
						|
			strcpy(cur, "...");
 | 
						|
			cur += 3;
 | 
						|
			total_len -= 3;
 | 
						|
			ao2_ref(fmt, -1);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		sprintf(cur, "%s%s", x ? "|" : "", name);
 | 
						|
		cur += name_len;
 | 
						|
		total_len -= name_len;
 | 
						|
 | 
						|
		ao2_ref(fmt, -1);
 | 
						|
	}
 | 
						|
	ao2_ref(cap, -1);
 | 
						|
 | 
						|
	/* These two characters have already been accounted for total_len purposes */
 | 
						|
	cur[0] = ')';
 | 
						|
	cur[1] = '\0';
 | 
						|
 | 
						|
	return size - total_len;
 | 
						|
}
 | 
						|
 | 
						|
static void codec_pref_remove_index(struct iax2_codec_pref *pref, int codec_pref_index)
 | 
						|
{
 | 
						|
	int idx;
 | 
						|
 | 
						|
	idx = codec_pref_index;
 | 
						|
	if (idx == ARRAY_LEN(pref->order) - 1) {
 | 
						|
		/* Remove from last array entry. */
 | 
						|
		pref->order[idx] = 0;
 | 
						|
		pref->framing[idx] = 0;
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	for (; idx < ARRAY_LEN(pref->order); ++idx) {
 | 
						|
		pref->order[idx] = pref->order[idx + 1];
 | 
						|
		pref->framing[idx] = pref->framing[idx + 1];
 | 
						|
		if (!pref->order[idx]) {
 | 
						|
			return;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/*! \brief Remove codec from pref list */
 | 
						|
static void codec_pref_remove(struct iax2_codec_pref *pref, int format_index)
 | 
						|
{
 | 
						|
	int x;
 | 
						|
 | 
						|
	if (!pref->order[0]) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	for (x = 0; x < ARRAY_LEN(pref->order); ++x) {
 | 
						|
		if (!pref->order[x]) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		if (pref->order[x] == format_index) {
 | 
						|
			codec_pref_remove_index(pref, x);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void iax2_codec_pref_remove_missing(struct iax2_codec_pref *pref, uint64_t bitfield)
 | 
						|
{
 | 
						|
	int idx;
 | 
						|
 | 
						|
	if (!pref->order[0]) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Work from the end of the list so we always deal with
 | 
						|
	 * unmodified entries in case we have to remove a pref.
 | 
						|
	 */
 | 
						|
	for (idx = ARRAY_LEN(pref->order); idx--;) {
 | 
						|
		uint64_t pref_bitfield;
 | 
						|
 | 
						|
		pref_bitfield = iax2_codec_pref_order_value_to_format_bitfield(pref->order[idx]);
 | 
						|
		if (!pref_bitfield) {
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		/* If this format isn't in the bitfield, remove it from the prefs. */
 | 
						|
		if (!(pref_bitfield & bitfield)) {
 | 
						|
			codec_pref_remove_index(pref, idx);
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/*!
 | 
						|
 * \brief Formats supported by IAX2.
 | 
						|
 *
 | 
						|
 * \note All AST_FORMAT_xxx compatibility bit defines must be
 | 
						|
 *  represented here.
 | 
						|
 *
 | 
						|
 * \note The order is important because the array index+1 values
 | 
						|
 * go out over the wire.
 | 
						|
 */
 | 
						|
static const uint64_t iax2_supported_formats[] = {
 | 
						|
	AST_FORMAT_G723,
 | 
						|
	AST_FORMAT_GSM,
 | 
						|
	AST_FORMAT_ULAW,
 | 
						|
	AST_FORMAT_ALAW,
 | 
						|
	AST_FORMAT_G726,
 | 
						|
	AST_FORMAT_ADPCM,
 | 
						|
	AST_FORMAT_SLIN,
 | 
						|
	AST_FORMAT_LPC10,
 | 
						|
	AST_FORMAT_G729,
 | 
						|
	AST_FORMAT_SPEEX,
 | 
						|
	AST_FORMAT_SPEEX16,
 | 
						|
	AST_FORMAT_ILBC,
 | 
						|
	AST_FORMAT_G726_AAL2,
 | 
						|
	AST_FORMAT_G722,
 | 
						|
	AST_FORMAT_SLIN16,
 | 
						|
	AST_FORMAT_JPEG,
 | 
						|
	AST_FORMAT_PNG,
 | 
						|
	AST_FORMAT_H261,
 | 
						|
	AST_FORMAT_H263,
 | 
						|
	AST_FORMAT_H263P,
 | 
						|
	AST_FORMAT_H264,
 | 
						|
	AST_FORMAT_MP4,
 | 
						|
	AST_FORMAT_T140_RED,
 | 
						|
	AST_FORMAT_T140,
 | 
						|
	AST_FORMAT_SIREN7,
 | 
						|
	AST_FORMAT_SIREN14,
 | 
						|
	AST_FORMAT_TESTLAW,
 | 
						|
	AST_FORMAT_G719,
 | 
						|
	0, /* Place holder */
 | 
						|
	0, /* Place holder */
 | 
						|
	0, /* Place holder */
 | 
						|
	0, /* Place holder */
 | 
						|
	0, /* Place holder */
 | 
						|
	0, /* Place holder */
 | 
						|
	0, /* Place holder */
 | 
						|
	0, /* Place holder */
 | 
						|
	AST_FORMAT_OPUS,
 | 
						|
	AST_FORMAT_VP8,
 | 
						|
	/* ONLY ADD TO THE END OF THIS LIST */
 | 
						|
	/* XXX Use up the place holder slots first. */
 | 
						|
};
 | 
						|
 | 
						|
uint64_t iax2_codec_pref_order_value_to_format_bitfield(int order_value)
 | 
						|
{
 | 
						|
	if (order_value < 1 || ARRAY_LEN(iax2_supported_formats) < order_value) {
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	return iax2_supported_formats[order_value - 1];
 | 
						|
}
 | 
						|
 | 
						|
int iax2_codec_pref_format_bitfield_to_order_value(uint64_t bitfield)
 | 
						|
{
 | 
						|
	int idx;
 | 
						|
 | 
						|
	if (bitfield) {
 | 
						|
		for (idx = 0; idx < ARRAY_LEN(iax2_supported_formats); ++idx) {
 | 
						|
			if (iax2_supported_formats[idx] == bitfield) {
 | 
						|
				return idx + 1;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*!
 | 
						|
 * \internal
 | 
						|
 * \brief Append the bitfield format to the codec preference list.
 | 
						|
 * \since 13.0.0
 | 
						|
 *
 | 
						|
 * \param pref Codec preference list to append the given bitfield.
 | 
						|
 * \param bitfield Format bitfield to append.
 | 
						|
 * \param framing Framing size of the codec.
 | 
						|
 */
 | 
						|
static void iax2_codec_pref_append_bitfield(struct iax2_codec_pref *pref, uint64_t bitfield, unsigned int framing)
 | 
						|
{
 | 
						|
	int format_index;
 | 
						|
	int x;
 | 
						|
 | 
						|
	format_index = iax2_codec_pref_format_bitfield_to_order_value(bitfield);
 | 
						|
	if (!format_index) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	codec_pref_remove(pref, format_index);
 | 
						|
 | 
						|
	for (x = 0; x < ARRAY_LEN(pref->order); ++x) {
 | 
						|
		if (!pref->order[x]) {
 | 
						|
			pref->order[x] = format_index;
 | 
						|
			pref->framing[x] = framing;
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void iax2_codec_pref_append(struct iax2_codec_pref *pref, struct ast_format *format, unsigned int framing)
 | 
						|
{
 | 
						|
	uint64_t bitfield;
 | 
						|
 | 
						|
	bitfield = ast_format_compatibility_format2bitfield(format);
 | 
						|
	if (!bitfield) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	iax2_codec_pref_append_bitfield(pref, bitfield, framing);
 | 
						|
}
 | 
						|
 | 
						|
void iax2_codec_pref_prepend(struct iax2_codec_pref *pref, struct ast_format *format, unsigned int framing,
 | 
						|
	int only_if_existing)
 | 
						|
{
 | 
						|
	uint64_t bitfield;
 | 
						|
	int format_index;
 | 
						|
	int x;
 | 
						|
 | 
						|
	bitfield = ast_format_compatibility_format2bitfield(format);
 | 
						|
	if (!bitfield) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	format_index = iax2_codec_pref_format_bitfield_to_order_value(bitfield);
 | 
						|
	if (!format_index) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Now find any existing occurrence, or the end */
 | 
						|
	for (x = 0; x < ARRAY_LEN(pref->order); ++x) {
 | 
						|
		if (!pref->order[x] || pref->order[x] == format_index)
 | 
						|
			break;
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	 * The array can never be full without format_index
 | 
						|
	 * also being in the array.
 | 
						|
	 */
 | 
						|
	ast_assert(x < ARRAY_LEN(pref->order));
 | 
						|
 | 
						|
	/* If we failed to find any occurrence, set to the end for safety. */
 | 
						|
	if (ARRAY_LEN(pref->order) <= x) {
 | 
						|
		x = ARRAY_LEN(pref->order) - 1;
 | 
						|
	}
 | 
						|
 | 
						|
	if (only_if_existing && !pref->order[x]) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Move down to make space to insert - either all the way to the end,
 | 
						|
	   or as far as the existing location (which will be overwritten) */
 | 
						|
	for (; x > 0; --x) {
 | 
						|
		pref->order[x] = pref->order[x - 1];
 | 
						|
		pref->framing[x] = pref->framing[x - 1];
 | 
						|
	}
 | 
						|
 | 
						|
	/* And insert the new entry */
 | 
						|
	pref->order[0] = format_index;
 | 
						|
	pref->framing[0] = framing;
 | 
						|
}
 | 
						|
 | 
						|
uint64_t iax2_codec_pref_from_bitfield(struct iax2_codec_pref *pref, uint64_t bitfield)
 | 
						|
{
 | 
						|
	int bit;
 | 
						|
	uint64_t working_bitfield;
 | 
						|
	uint64_t best_bitfield;
 | 
						|
	struct ast_format *format;
 | 
						|
 | 
						|
	/* Init the preference list. */
 | 
						|
	memset(pref, 0, sizeof(*pref));
 | 
						|
 | 
						|
	working_bitfield = bitfield;
 | 
						|
 | 
						|
	/* Add the "best" codecs first. */
 | 
						|
	while (working_bitfield) {
 | 
						|
		best_bitfield = iax2_format_compatibility_best(working_bitfield);
 | 
						|
		if (!best_bitfield) {
 | 
						|
			/* No more codecs considered best. */
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		/* Remove current "best" codec to find the next "best". */
 | 
						|
		working_bitfield &= ~best_bitfield;
 | 
						|
 | 
						|
		format = ast_format_compatibility_bitfield2format(best_bitfield);
 | 
						|
		/* The best_bitfield should always be convertible to a format. */
 | 
						|
		ast_assert(format != NULL);
 | 
						|
 | 
						|
		iax2_codec_pref_append_bitfield(pref, best_bitfield, 0);
 | 
						|
	}
 | 
						|
 | 
						|
	/* Add any remaining codecs. */
 | 
						|
	if (working_bitfield) {
 | 
						|
		for (bit = 0; bit < 64; ++bit) {
 | 
						|
			uint64_t mask = (1ULL << bit);
 | 
						|
 | 
						|
			if (mask & working_bitfield) {
 | 
						|
				format = ast_format_compatibility_bitfield2format(mask);
 | 
						|
				if (!format) {
 | 
						|
					/* The bit is not associated with any format. */
 | 
						|
					bitfield &= ~mask;
 | 
						|
					continue;
 | 
						|
				}
 | 
						|
 | 
						|
				iax2_codec_pref_append_bitfield(pref, mask, 0);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return bitfield;
 | 
						|
}
 |