mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-31 10:47:18 +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,
 | |
| 	0, /* reserved; was 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;
 | |
| }
 |