mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-11-04 05:15:22 +00:00 
			
		
		
		
	ASTERISK_REGISTER_FILE no longer has any purpose so this commit removes all traces of it. Previously exported symbols removed: * __ast_register_file * __ast_unregister_file * ast_complete_source_filename This also removes the mtx_prof static variable that was declared when MTX_PROFILE was enabled. This variable was only used in lock.c so it is now initialized in that file only. ASTERISK-26480 #close Change-Id: I1074af07d71f9e159c48ef36631aa432c86f9966
		
			
				
	
	
		
			411 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			411 lines
		
	
	
		
			10 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 Codecs API
 | 
						|
 *
 | 
						|
 * \author Joshua Colp <jcolp@digium.com>
 | 
						|
 */
 | 
						|
 | 
						|
/*** MODULEINFO
 | 
						|
	<support_level>core</support_level>
 | 
						|
 ***/
 | 
						|
 | 
						|
#include "asterisk.h"
 | 
						|
 | 
						|
#include "asterisk/logger.h"
 | 
						|
#include "asterisk/codec.h"
 | 
						|
#include "asterisk/format.h"
 | 
						|
#include "asterisk/frame.h"
 | 
						|
#include "asterisk/astobj2.h"
 | 
						|
#include "asterisk/strings.h"
 | 
						|
#include "asterisk/module.h"
 | 
						|
#include "asterisk/cli.h"
 | 
						|
 | 
						|
/*! \brief Number of buckets to use for codecs (should be prime for performance reasons) */
 | 
						|
#define CODEC_BUCKETS 53
 | 
						|
 | 
						|
/*! \brief Current identifier value for newly registered codec */
 | 
						|
static int codec_id = 1;
 | 
						|
 | 
						|
/*! \brief Registered codecs */
 | 
						|
static struct ao2_container *codecs;
 | 
						|
 | 
						|
/*!
 | 
						|
 * \internal
 | 
						|
 * \brief Internal codec structure
 | 
						|
 *
 | 
						|
 * External codecs won't know about the format_name field so the public
 | 
						|
 * ast_codec structure has to leave it out.  This structure will be used
 | 
						|
 * for the internal codecs.
 | 
						|
 *
 | 
						|
 */
 | 
						|
struct internal_ast_codec {
 | 
						|
	/*! \brief Public codec structure.  Must remain first. */
 | 
						|
	struct ast_codec external;
 | 
						|
	/*! \brief A format name for a default sane format using this codec */
 | 
						|
	const char *format_name;
 | 
						|
};
 | 
						|
 | 
						|
/*!
 | 
						|
 * \internal
 | 
						|
 * \brief Internal function for registration with format name
 | 
						|
 *
 | 
						|
 * This function is only used by codec.c and codec_builtin.c and
 | 
						|
 * will be removed in Asterisk 14
 | 
						|
 */
 | 
						|
int __ast_codec_register_with_format(struct ast_codec *codec, const char *format_name,
 | 
						|
	struct ast_module *mod);
 | 
						|
 | 
						|
static int codec_hash(const void *obj, int flags)
 | 
						|
{
 | 
						|
	const struct ast_codec *codec;
 | 
						|
	const char *key;
 | 
						|
 | 
						|
	switch (flags & OBJ_SEARCH_MASK) {
 | 
						|
	case OBJ_SEARCH_KEY:
 | 
						|
		key = obj;
 | 
						|
		return ast_str_hash(key);
 | 
						|
	case OBJ_SEARCH_OBJECT:
 | 
						|
		codec = obj;
 | 
						|
		return ast_str_hash(codec->name);
 | 
						|
	default:
 | 
						|
		/* Hash can only work on something with a full key. */
 | 
						|
		ast_assert(0);
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static int codec_cmp(void *obj, void *arg, int flags)
 | 
						|
{
 | 
						|
	const struct ast_codec *left = obj;
 | 
						|
	const struct ast_codec *right = arg;
 | 
						|
	const char *right_key = arg;
 | 
						|
	int cmp;
 | 
						|
 | 
						|
	switch (flags & OBJ_SEARCH_MASK) {
 | 
						|
	case OBJ_SEARCH_OBJECT:
 | 
						|
		right_key = right->name;
 | 
						|
		cmp = strcmp(left->name, right_key);
 | 
						|
 | 
						|
		if (right->type != AST_MEDIA_TYPE_UNKNOWN) {
 | 
						|
			cmp |= (right->type != left->type);
 | 
						|
		}
 | 
						|
 | 
						|
		/* BUGBUG: this will allow a match on a codec by name only.
 | 
						|
		 * This is particularly useful when executed by the CLI; if
 | 
						|
		 * that is not needed in translate.c, this can be removed.
 | 
						|
		 */
 | 
						|
		if (right->sample_rate) {
 | 
						|
			cmp |= (right->sample_rate != left->sample_rate);
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	case OBJ_SEARCH_KEY:
 | 
						|
		cmp = strcmp(left->name, right_key);
 | 
						|
		break;
 | 
						|
	case OBJ_SEARCH_PARTIAL_KEY:
 | 
						|
		cmp = strncmp(left->name, right_key, strlen(right_key));
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		ast_assert(0);
 | 
						|
		cmp = 0;
 | 
						|
		break;
 | 
						|
	}
 | 
						|
	if (cmp) {
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	return CMP_MATCH;
 | 
						|
}
 | 
						|
 | 
						|
static char *show_codecs(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 | 
						|
{
 | 
						|
	struct ao2_iterator i;
 | 
						|
	struct internal_ast_codec *codec;
 | 
						|
 | 
						|
	switch (cmd) {
 | 
						|
	case CLI_INIT:
 | 
						|
		e->command = "core show codecs [audio|video|image|text]";
 | 
						|
		e->usage =
 | 
						|
			"Usage: core show codecs [audio|video|image|text]\n"
 | 
						|
			"       Displays codec mapping\n";
 | 
						|
		return NULL;
 | 
						|
	case CLI_GENERATE:
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	if ((a->argc < 3) || (a->argc > 4)) {
 | 
						|
		return CLI_SHOWUSAGE;
 | 
						|
	}
 | 
						|
 | 
						|
	if (!ast_opt_dont_warn) {
 | 
						|
		ast_cli(a->fd, "Disclaimer: this command is for informational purposes only.\n"
 | 
						|
				"\tIt does not indicate anything about your configuration.\n");
 | 
						|
	}
 | 
						|
 | 
						|
	ast_cli(a->fd, "%8s %-5s %-12s %-16s %s\n","ID","TYPE","NAME","FORMAT","DESCRIPTION");
 | 
						|
	ast_cli(a->fd, "------------------------------------------------------------------------------------------------\n");
 | 
						|
 | 
						|
	ao2_rdlock(codecs);
 | 
						|
	i = ao2_iterator_init(codecs, AO2_ITERATOR_DONTLOCK);
 | 
						|
 | 
						|
	for (; (codec = ao2_iterator_next(&i)); ao2_ref(codec, -1)) {
 | 
						|
		if (a->argc == 4) {
 | 
						|
			if (!strcasecmp(a->argv[3], "audio")) {
 | 
						|
				if (codec->external.type != AST_MEDIA_TYPE_AUDIO) {
 | 
						|
					continue;
 | 
						|
				}
 | 
						|
			} else if (!strcasecmp(a->argv[3], "video")) {
 | 
						|
				if (codec->external.type != AST_MEDIA_TYPE_VIDEO) {
 | 
						|
					continue;
 | 
						|
				}
 | 
						|
			} else if (!strcasecmp(a->argv[3], "image")) {
 | 
						|
				if (codec->external.type != AST_MEDIA_TYPE_IMAGE) {
 | 
						|
					continue;
 | 
						|
				}
 | 
						|
			} else if (!strcasecmp(a->argv[3], "text")) {
 | 
						|
				if (codec->external.type != AST_MEDIA_TYPE_TEXT) {
 | 
						|
					continue;
 | 
						|
				}
 | 
						|
			} else {
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		ast_cli(a->fd, "%8u %-5s %-12s %-16s (%s)\n",
 | 
						|
			codec->external.id,
 | 
						|
			ast_codec_media_type2str(codec->external.type),
 | 
						|
			codec->external.name,
 | 
						|
			S_OR(codec->format_name, "no cached format"),
 | 
						|
			codec->external.description);
 | 
						|
	}
 | 
						|
 | 
						|
	ao2_iterator_destroy(&i);
 | 
						|
	ao2_unlock(codecs);
 | 
						|
 | 
						|
	return CLI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/*! \brief Callback function for getting a codec based on unique identifier */
 | 
						|
static int codec_id_cmp(void *obj, void *arg, int flags)
 | 
						|
{
 | 
						|
	struct ast_codec *codec = obj;
 | 
						|
	int *id = arg;
 | 
						|
 | 
						|
	return (codec->id == *id) ? CMP_MATCH | CMP_STOP : 0;
 | 
						|
}
 | 
						|
 | 
						|
static char *show_codec(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 | 
						|
{
 | 
						|
	int type_punned_codec;
 | 
						|
	struct internal_ast_codec *codec;
 | 
						|
 | 
						|
	switch (cmd) {
 | 
						|
	case CLI_INIT:
 | 
						|
		e->command = "core show codec";
 | 
						|
		e->usage =
 | 
						|
			"Usage: core show codec <number>\n"
 | 
						|
			"       Displays codec mapping\n";
 | 
						|
		return NULL;
 | 
						|
	case CLI_GENERATE:
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	if (a->argc != 4) {
 | 
						|
		return CLI_SHOWUSAGE;
 | 
						|
	}
 | 
						|
 | 
						|
	if (sscanf(a->argv[3], "%30d", &type_punned_codec) != 1) {
 | 
						|
		return CLI_SHOWUSAGE;
 | 
						|
	}
 | 
						|
 | 
						|
	codec = ao2_callback(codecs, 0, codec_id_cmp, &type_punned_codec);
 | 
						|
	if (!codec) {
 | 
						|
		ast_cli(a->fd, "Codec %d not found\n", type_punned_codec);
 | 
						|
		return CLI_SUCCESS;
 | 
						|
	}
 | 
						|
 | 
						|
	ast_cli(a->fd, "%11u %s (%s)\n", (unsigned int) codec->external.id, codec->external.description,
 | 
						|
		S_OR(codec->format_name, "no format"));
 | 
						|
 | 
						|
	ao2_ref(codec, -1);
 | 
						|
 | 
						|
	return CLI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/* Builtin Asterisk CLI-commands for debugging */
 | 
						|
static struct ast_cli_entry codec_cli[] = {
 | 
						|
	AST_CLI_DEFINE(show_codecs, "Displays a list of registered codecs"),
 | 
						|
	AST_CLI_DEFINE(show_codec, "Shows a specific codec"),
 | 
						|
};
 | 
						|
 | 
						|
/*! \brief Function called when the process is shutting down */
 | 
						|
static void codec_shutdown(void)
 | 
						|
{
 | 
						|
	ast_cli_unregister_multiple(codec_cli, ARRAY_LEN(codec_cli));
 | 
						|
	ao2_cleanup(codecs);
 | 
						|
	codecs = NULL;
 | 
						|
}
 | 
						|
 | 
						|
int ast_codec_init(void)
 | 
						|
{
 | 
						|
	codecs = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK, CODEC_BUCKETS, codec_hash, codec_cmp);
 | 
						|
	if (!codecs) {
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	ast_cli_register_multiple(codec_cli, ARRAY_LEN(codec_cli));
 | 
						|
	ast_register_cleanup(codec_shutdown);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void codec_dtor(void *obj)
 | 
						|
{
 | 
						|
	struct ast_codec *codec;
 | 
						|
 | 
						|
	codec = obj;
 | 
						|
 | 
						|
	ast_module_unref(codec->mod);
 | 
						|
}
 | 
						|
 | 
						|
int __ast_codec_register(struct ast_codec *codec, struct ast_module *mod)
 | 
						|
{
 | 
						|
	return __ast_codec_register_with_format(codec, NULL, mod);
 | 
						|
}
 | 
						|
 | 
						|
int __ast_codec_register_with_format(struct ast_codec *codec, const char *format_name, struct ast_module *mod)
 | 
						|
{
 | 
						|
	SCOPED_AO2WRLOCK(lock, codecs);
 | 
						|
	struct internal_ast_codec *codec_new;
 | 
						|
 | 
						|
	/* Some types have specific requirements */
 | 
						|
	if (codec->type == AST_MEDIA_TYPE_UNKNOWN) {
 | 
						|
		ast_log(LOG_ERROR, "A media type must be specified for codec '%s'\n", codec->name);
 | 
						|
		return -1;
 | 
						|
	} else if (codec->type == AST_MEDIA_TYPE_AUDIO) {
 | 
						|
		if (!codec->sample_rate) {
 | 
						|
			ast_log(LOG_ERROR, "A sample rate must be specified for codec '%s' of type '%s'\n",
 | 
						|
				codec->name, ast_codec_media_type2str(codec->type));
 | 
						|
			return -1;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	codec_new = ao2_find(codecs, codec, OBJ_SEARCH_OBJECT | OBJ_NOLOCK);
 | 
						|
	if (codec_new) {
 | 
						|
		ast_log(LOG_ERROR, "A codec with name '%s' of type '%s' and sample rate '%u' is already registered\n",
 | 
						|
			codec->name, ast_codec_media_type2str(codec->type), codec->sample_rate);
 | 
						|
		ao2_ref(codec_new, -1);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	codec_new = ao2_t_alloc_options(sizeof(*codec_new), codec_dtor,
 | 
						|
		AO2_ALLOC_OPT_LOCK_NOLOCK, S_OR(codec->description, ""));
 | 
						|
	if (!codec_new) {
 | 
						|
		ast_log(LOG_ERROR, "Could not allocate a codec with name '%s' of type '%s' and sample rate '%u'\n",
 | 
						|
			codec->name, ast_codec_media_type2str(codec->type), codec->sample_rate);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
	codec_new->external = *codec;
 | 
						|
	codec_new->format_name = format_name;
 | 
						|
	codec_new->external.id = codec_id++;
 | 
						|
 | 
						|
	ao2_link_flags(codecs, codec_new, OBJ_NOLOCK);
 | 
						|
 | 
						|
	/* Once registered a codec can not be unregistered, and the module must persist until shutdown */
 | 
						|
	ast_module_shutdown_ref(mod);
 | 
						|
 | 
						|
	ast_verb(2, "Registered '%s' codec '%s' at sample rate '%u' with id '%u'\n",
 | 
						|
		ast_codec_media_type2str(codec->type), codec->name, codec->sample_rate, codec_new->external.id);
 | 
						|
 | 
						|
	ao2_ref(codec_new, -1);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
struct ast_codec *ast_codec_get(const char *name, enum ast_media_type type, unsigned int sample_rate)
 | 
						|
{
 | 
						|
	struct ast_codec codec = {
 | 
						|
		.name = name,
 | 
						|
		.type = type,
 | 
						|
		.sample_rate = sample_rate,
 | 
						|
	};
 | 
						|
 | 
						|
	return ao2_find(codecs, &codec, OBJ_SEARCH_OBJECT);
 | 
						|
}
 | 
						|
 | 
						|
struct ast_codec *ast_codec_get_by_id(int id)
 | 
						|
{
 | 
						|
	return ao2_callback(codecs, 0, codec_id_cmp, &id);
 | 
						|
}
 | 
						|
 | 
						|
int ast_codec_get_max(void)
 | 
						|
{
 | 
						|
	return codec_id;
 | 
						|
}
 | 
						|
 | 
						|
const char *ast_codec_media_type2str(enum ast_media_type type)
 | 
						|
{
 | 
						|
	switch (type) {
 | 
						|
	case AST_MEDIA_TYPE_AUDIO:
 | 
						|
		return "audio";
 | 
						|
	case AST_MEDIA_TYPE_VIDEO:
 | 
						|
		return "video";
 | 
						|
	case AST_MEDIA_TYPE_IMAGE:
 | 
						|
		return "image";
 | 
						|
	case AST_MEDIA_TYPE_TEXT:
 | 
						|
		return "text";
 | 
						|
	default:
 | 
						|
		return "<unknown>";
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
unsigned int ast_codec_samples_count(struct ast_frame *frame)
 | 
						|
{
 | 
						|
	struct ast_codec *codec;
 | 
						|
	unsigned int samples = 0;
 | 
						|
 | 
						|
	if ((frame->frametype != AST_FRAME_VOICE) &&
 | 
						|
		(frame->frametype != AST_FRAME_VIDEO) &&
 | 
						|
		(frame->frametype != AST_FRAME_IMAGE)) {
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	codec = ast_format_get_codec(frame->subclass.format);
 | 
						|
 | 
						|
	if (codec->samples_count) {
 | 
						|
		samples = codec->samples_count(frame);
 | 
						|
	} else {
 | 
						|
		ast_log(LOG_WARNING, "Unable to calculate samples for codec %s\n",
 | 
						|
			ast_format_get_name(frame->subclass.format));
 | 
						|
	}
 | 
						|
 | 
						|
	ao2_ref(codec, -1);
 | 
						|
	return samples;
 | 
						|
}
 | 
						|
 | 
						|
unsigned int ast_codec_determine_length(const struct ast_codec *codec, unsigned int samples)
 | 
						|
{
 | 
						|
	if (!codec->get_length) {
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	return codec->get_length(samples);
 | 
						|
}
 |