Skip to content
Snippets Groups Projects
rtp_engine.c 70.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • Joshua Colp's avatar
    Joshua Colp committed
    /*
     * Asterisk -- An open source telephony toolkit.
     *
     * Copyright (C) 1999 - 2008, 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 Pluggable RTP Architecture
     *
     * \author Joshua Colp <jcolp@digium.com>
     */
    
    #include "asterisk.h"
    
    ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
    
    #include <math.h>
    
    #include "asterisk/channel.h"
    #include "asterisk/frame.h"
    #include "asterisk/module.h"
    #include "asterisk/rtp_engine.h"
    #include "asterisk/manager.h"
    #include "asterisk/options.h"
    #include "asterisk/astobj2.h"
    #include "asterisk/pbx.h"
    
    Mark Michelson's avatar
    Mark Michelson committed
    #include "asterisk/netsock2.h"
    
    struct ast_srtp_res *res_srtp = NULL;
    struct ast_srtp_policy_res *res_srtp_policy = NULL;
    
    
    Joshua Colp's avatar
    Joshua Colp committed
    /*! Structure that represents an RTP session (instance) */
    struct ast_rtp_instance {
    	/*! Engine that is handling this RTP instance */
    	struct ast_rtp_engine *engine;
    	/*! Data unique to the RTP engine */
    	void *data;
    	/*! RTP properties that have been set and their value */
    	int properties[AST_RTP_PROPERTY_MAX];
    	/*! Address that we are expecting RTP to come in to */
    
    Mark Michelson's avatar
    Mark Michelson committed
    	struct ast_sockaddr local_address;
    
    Joshua Colp's avatar
    Joshua Colp committed
    	/*! Address that we are sending RTP to */
    
    Mark Michelson's avatar
    Mark Michelson committed
    	struct ast_sockaddr remote_address;
    
    	/*! Alternate address that we are receiving RTP from */
    
    Mark Michelson's avatar
    Mark Michelson committed
    	struct ast_sockaddr alt_remote_address;
    
    Joshua Colp's avatar
    Joshua Colp committed
    	/*! Instance that we are bridged to if doing remote or local bridging */
    	struct ast_rtp_instance *bridged;
    	/*! Payload and packetization information */
    	struct ast_rtp_codecs codecs;
    	/*! RTP timeout time (negative or zero means disabled, negative value means temporarily disabled) */
    	int timeout;
    	/*! RTP timeout when on hold (negative or zero means disabled, negative value means temporarily disabled). */
    	int holdtimeout;
    
    	/*! RTP keepalive interval */
    	int keepalive;
    
    Joshua Colp's avatar
    Joshua Colp committed
    	/*! DTMF mode in use */
    	enum ast_rtp_dtmf_mode dtmf_mode;
    
    	/*! Glue currently in use */
    	struct ast_rtp_glue *glue;
    	/*! Channel associated with the instance */
    	struct ast_channel *chan;
    
    	/*! SRTP info associated with the instance */
    	struct ast_srtp *srtp;
    
    Joshua Colp's avatar
    Joshua Colp committed
    };
    
    /*! List of RTP engines that are currently registered */
    static AST_RWLIST_HEAD_STATIC(engines, ast_rtp_engine);
    
    /*! List of RTP glues */
    static AST_RWLIST_HEAD_STATIC(glues, ast_rtp_glue);
    
    /*! The following array defines the MIME Media type (and subtype) for each
       of our codecs, or RTP-specific data type. */
    
    Joshua Colp's avatar
    Joshua Colp committed
    	struct ast_rtp_payload_type payload_type;
    	char *type;
    	char *subtype;
    	unsigned int sample_rate;
    
    } ast_rtp_mime_types[128]; /* This will Likely not need to grow any time soon. */
    static ast_rwlock_t mime_types_lock;
    static int mime_types_len = 0;
    
    Joshua Colp's avatar
    Joshua Colp committed
    
    /*!
     * \brief Mapping between Asterisk codecs and rtp payload types
     *
     * Static (i.e., well-known) RTP payload types for our "AST_FORMAT..."s:
     * also, our own choices for dynamic payload types.  This is our master
     * table for transmission
     *
     * See http://www.iana.org/assignments/rtp-parameters for a list of
     * assigned values
     */
    
    static struct ast_rtp_payload_type static_RTP_PT[AST_RTP_MAX_PT];
    static ast_rwlock_t static_RTP_PT_lock;
    
    Joshua Colp's avatar
    Joshua Colp committed
    
    int ast_rtp_engine_register2(struct ast_rtp_engine *engine, struct ast_module *module)
    {
    	struct ast_rtp_engine *current_engine;
    
    	/* Perform a sanity check on the engine structure to make sure it has the basics */
    	if (ast_strlen_zero(engine->name) || !engine->new || !engine->destroy || !engine->write || !engine->read) {
    		ast_log(LOG_WARNING, "RTP Engine '%s' failed sanity check so it was not registered.\n", !ast_strlen_zero(engine->name) ? engine->name : "Unknown");
    		return -1;
    	}
    
    	/* Link owner module to the RTP engine for reference counting purposes */
    	engine->mod = module;
    
    	AST_RWLIST_WRLOCK(&engines);
    
    	/* Ensure that no two modules with the same name are registered at the same time */
    	AST_RWLIST_TRAVERSE(&engines, current_engine, entry) {
    		if (!strcmp(current_engine->name, engine->name)) {
    			ast_log(LOG_WARNING, "An RTP engine with the name '%s' has already been registered.\n", engine->name);
    			AST_RWLIST_UNLOCK(&engines);
    			return -1;
    		}
    	}
    
    	/* The engine survived our critique. Off to the list it goes to be used */
    	AST_RWLIST_INSERT_TAIL(&engines, engine, entry);
    
    	AST_RWLIST_UNLOCK(&engines);
    
    	ast_verb(2, "Registered RTP engine '%s'\n", engine->name);
    
    	return 0;
    }
    
    int ast_rtp_engine_unregister(struct ast_rtp_engine *engine)
    {
    	struct ast_rtp_engine *current_engine = NULL;
    
    	AST_RWLIST_WRLOCK(&engines);
    
    	if ((current_engine = AST_RWLIST_REMOVE(&engines, engine, entry))) {
    		ast_verb(2, "Unregistered RTP engine '%s'\n", engine->name);
    	}
    
    	AST_RWLIST_UNLOCK(&engines);
    
    	return current_engine ? 0 : -1;
    }
    
    int ast_rtp_glue_register2(struct ast_rtp_glue *glue, struct ast_module *module)
    {
    	struct ast_rtp_glue *current_glue = NULL;
    
    	if (ast_strlen_zero(glue->type)) {
    		return -1;
    	}
    
    	glue->mod = module;
    
    	AST_RWLIST_WRLOCK(&glues);
    
    	AST_RWLIST_TRAVERSE(&glues, current_glue, entry) {
    		if (!strcasecmp(current_glue->type, glue->type)) {
    			ast_log(LOG_WARNING, "RTP glue with the name '%s' has already been registered.\n", glue->type);
    			AST_RWLIST_UNLOCK(&glues);
    			return -1;
    		}
    	}
    
    	AST_RWLIST_INSERT_TAIL(&glues, glue, entry);
    
    	AST_RWLIST_UNLOCK(&glues);
    
    	ast_verb(2, "Registered RTP glue '%s'\n", glue->type);
    
    	return 0;
    }
    
    int ast_rtp_glue_unregister(struct ast_rtp_glue *glue)
    {
    	struct ast_rtp_glue *current_glue = NULL;
    
    	AST_RWLIST_WRLOCK(&glues);
    
    	if ((current_glue = AST_RWLIST_REMOVE(&glues, glue, entry))) {
    		ast_verb(2, "Unregistered RTP glue '%s'\n", glue->type);
    	}
    
    	AST_RWLIST_UNLOCK(&glues);
    
    	return current_glue ? 0 : -1;
    }
    
    static void instance_destructor(void *obj)
    {
    	struct ast_rtp_instance *instance = obj;
    
    	/* Pass us off to the engine to destroy */
    	if (instance->data && instance->engine->destroy(instance)) {
    		ast_debug(1, "Engine '%s' failed to destroy RTP instance '%p'\n", instance->engine->name, instance);
    		return;
    	}
    
    
    	if (instance->srtp) {
    		res_srtp->destroy(instance->srtp);
    	}
    
    
    Joshua Colp's avatar
    Joshua Colp committed
    	/* Drop our engine reference */
    	ast_module_unref(instance->engine->mod);
    
    	ast_debug(1, "Destroyed RTP instance '%p'\n", instance);
    }
    
    int ast_rtp_instance_destroy(struct ast_rtp_instance *instance)
    {
    	ao2_ref(instance, -1);
    
    	return 0;
    }
    
    
    Mark Michelson's avatar
    Mark Michelson committed
    struct ast_rtp_instance *ast_rtp_instance_new(const char *engine_name,
    
    		struct ast_sched_context *sched, const struct ast_sockaddr *sa,
    
    Mark Michelson's avatar
    Mark Michelson committed
    		void *data)
    
    Mark Michelson's avatar
    Mark Michelson committed
    	struct ast_sockaddr address = {{0,}};
    
    Joshua Colp's avatar
    Joshua Colp committed
    	struct ast_rtp_instance *instance = NULL;
    	struct ast_rtp_engine *engine = NULL;
    
    	AST_RWLIST_RDLOCK(&engines);
    
    	/* If an engine name was specified try to use it or otherwise use the first one registered */
    	if (!ast_strlen_zero(engine_name)) {
    		AST_RWLIST_TRAVERSE(&engines, engine, entry) {
    			if (!strcmp(engine->name, engine_name)) {
    				break;
    			}
    		}
    	} else {
    		engine = AST_RWLIST_FIRST(&engines);
    	}
    
    	/* If no engine was actually found bail out now */
    	if (!engine) {
    		ast_log(LOG_ERROR, "No RTP engine was found. Do you have one loaded?\n");
    		AST_RWLIST_UNLOCK(&engines);
    		return NULL;
    	}
    
    	/* Bump up the reference count before we return so the module can not be unloaded */
    	ast_module_ref(engine->mod);
    
    	AST_RWLIST_UNLOCK(&engines);
    
    	/* Allocate a new RTP instance */
    	if (!(instance = ao2_alloc(sizeof(*instance), instance_destructor))) {
    		ast_module_unref(engine->mod);
    		return NULL;
    	}
    	instance->engine = engine;
    
    Mark Michelson's avatar
    Mark Michelson committed
    	ast_sockaddr_copy(&instance->local_address, sa);
    	ast_sockaddr_copy(&address, sa);
    
    Joshua Colp's avatar
    Joshua Colp committed
    
    	ast_debug(1, "Using engine '%s' for RTP instance '%p'\n", engine->name, instance);
    
    	/* And pass it off to the engine to setup */
    
    	if (instance->engine->new(instance, sched, &address, data)) {
    
    Joshua Colp's avatar
    Joshua Colp committed
    		ast_debug(1, "Engine '%s' failed to setup RTP instance '%p'\n", engine->name, instance);
    		ao2_ref(instance, -1);
    		return NULL;
    	}
    
    	ast_debug(1, "RTP instance '%p' is setup and ready to go\n", instance);
    
    	return instance;
    }
    
    void ast_rtp_instance_set_data(struct ast_rtp_instance *instance, void *data)
    {
    	instance->data = data;
    }
    
    void *ast_rtp_instance_get_data(struct ast_rtp_instance *instance)
    {
    	return instance->data;
    }
    
    int ast_rtp_instance_write(struct ast_rtp_instance *instance, struct ast_frame *frame)
    {
    	return instance->engine->write(instance, frame);
    }
    
    struct ast_frame *ast_rtp_instance_read(struct ast_rtp_instance *instance, int rtcp)
    {
    	return instance->engine->read(instance, rtcp);
    }
    
    
    Mark Michelson's avatar
    Mark Michelson committed
    int ast_rtp_instance_set_local_address(struct ast_rtp_instance *instance,
    		const struct ast_sockaddr *address)
    
    Mark Michelson's avatar
    Mark Michelson committed
    	ast_sockaddr_copy(&instance->local_address, address);
    
    Mark Michelson's avatar
    Mark Michelson committed
    int ast_rtp_instance_set_remote_address(struct ast_rtp_instance *instance,
    		const struct ast_sockaddr *address)
    
    Mark Michelson's avatar
    Mark Michelson committed
    	ast_sockaddr_copy(&instance->remote_address, address);
    
    Joshua Colp's avatar
    Joshua Colp committed
    
    	/* moo */
    
    	if (instance->engine->remote_address_set) {
    
    		instance->engine->remote_address_set(instance, &instance->remote_address);
    
    Mark Michelson's avatar
    Mark Michelson committed
    int ast_rtp_instance_set_alt_remote_address(struct ast_rtp_instance *instance,
    		const struct ast_sockaddr *address)
    
    Mark Michelson's avatar
    Mark Michelson committed
    	ast_sockaddr_copy(&instance->alt_remote_address, address);
    
    
    	/* oink */
    
    	if (instance->engine->alt_remote_address_set) {
    		instance->engine->alt_remote_address_set(instance, &instance->alt_remote_address);
    	}
    
    	return 0;
    }
    
    
    int ast_rtp_instance_get_and_cmp_local_address(struct ast_rtp_instance *instance,
    
    Mark Michelson's avatar
    Mark Michelson committed
    		struct ast_sockaddr *address)
    
    Mark Michelson's avatar
    Mark Michelson committed
    	if (ast_sockaddr_cmp(address, &instance->local_address) != 0) {
    		ast_sockaddr_copy(address, &instance->local_address);
    
    void ast_rtp_instance_get_local_address(struct ast_rtp_instance *instance,
    		struct ast_sockaddr *address)
    {
    	ast_sockaddr_copy(address, &instance->local_address);
    }
    
    int ast_rtp_instance_get_and_cmp_remote_address(struct ast_rtp_instance *instance,
    
    Mark Michelson's avatar
    Mark Michelson committed
    		struct ast_sockaddr *address)
    
    Mark Michelson's avatar
    Mark Michelson committed
    	if (ast_sockaddr_cmp(address, &instance->remote_address) != 0) {
    		ast_sockaddr_copy(address, &instance->remote_address);
    
    void ast_rtp_instance_get_remote_address(struct ast_rtp_instance *instance,
    		struct ast_sockaddr *address)
    {
    	ast_sockaddr_copy(address, &instance->remote_address);
    }
    
    
    Joshua Colp's avatar
    Joshua Colp committed
    void ast_rtp_instance_set_extended_prop(struct ast_rtp_instance *instance, int property, void *value)
    {
    	if (instance->engine->extended_prop_set) {
    		instance->engine->extended_prop_set(instance, property, value);
    	}
    }
    
    void *ast_rtp_instance_get_extended_prop(struct ast_rtp_instance *instance, int property)
    {
    	if (instance->engine->extended_prop_get) {
    		return instance->engine->extended_prop_get(instance, property);
    	}
    
    	return NULL;
    }
    
    void ast_rtp_instance_set_prop(struct ast_rtp_instance *instance, enum ast_rtp_property property, int value)
    {
    	instance->properties[property] = value;
    
    	if (instance->engine->prop_set) {
    		instance->engine->prop_set(instance, property, value);
    	}
    }
    
    int ast_rtp_instance_get_prop(struct ast_rtp_instance *instance, enum ast_rtp_property property)
    {
    	return instance->properties[property];
    }
    
    struct ast_rtp_codecs *ast_rtp_instance_get_codecs(struct ast_rtp_instance *instance)
    {
    	return &instance->codecs;
    }
    
    void ast_rtp_codecs_payloads_clear(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance)
    {
    	int i;
    
    	for (i = 0; i < AST_RTP_MAX_PT; i++) {
    		codecs->payloads[i].asterisk_format = 0;
    
    		codecs->payloads[i].rtp_code = 0;
    		ast_format_clear(&codecs->payloads[i].format);
    
    Joshua Colp's avatar
    Joshua Colp committed
    		if (instance && instance->engine && instance->engine->payload_set) {
    
    			instance->engine->payload_set(instance, i, 0, NULL, 0);
    
    Joshua Colp's avatar
    Joshua Colp committed
    		}
    	}
    }
    
    void ast_rtp_codecs_payloads_default(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance)
    {
    	int i;
    
    
    	ast_rwlock_rdlock(&static_RTP_PT_lock);
    
    Joshua Colp's avatar
    Joshua Colp committed
    	for (i = 0; i < AST_RTP_MAX_PT; i++) {
    
    		if (static_RTP_PT[i].rtp_code || static_RTP_PT[i].asterisk_format) {
    
    
    Joshua Colp's avatar
    Joshua Colp committed
    			codecs->payloads[i].asterisk_format = static_RTP_PT[i].asterisk_format;
    
    			codecs->payloads[i].rtp_code = static_RTP_PT[i].rtp_code;
    			ast_format_copy(&codecs->payloads[i].format, &static_RTP_PT[i].format);
    
    Joshua Colp's avatar
    Joshua Colp committed
    			if (instance && instance->engine && instance->engine->payload_set) {
    
    				instance->engine->payload_set(instance, i, codecs->payloads[i].asterisk_format, &codecs->payloads[i].format, codecs->payloads[i].rtp_code);
    
    	ast_rwlock_unlock(&static_RTP_PT_lock);
    
    Joshua Colp's avatar
    Joshua Colp committed
    }
    
    void ast_rtp_codecs_payloads_copy(struct ast_rtp_codecs *src, struct ast_rtp_codecs *dest, struct ast_rtp_instance *instance)
    {
    	int i;
    
    	for (i = 0; i < AST_RTP_MAX_PT; i++) {
    
    		if (src->payloads[i].rtp_code || src->payloads[i].asterisk_format) {
    
    Joshua Colp's avatar
    Joshua Colp committed
    			ast_debug(2, "Copying payload %d from %p to %p\n", i, src, dest);
    			dest->payloads[i].asterisk_format = src->payloads[i].asterisk_format;
    
    			dest->payloads[i].rtp_code = src->payloads[i].rtp_code;
    			ast_format_copy(&dest->payloads[i].format, &src->payloads[i].format);
    
    Joshua Colp's avatar
    Joshua Colp committed
    			if (instance && instance->engine && instance->engine->payload_set) {
    
    				instance->engine->payload_set(instance, i, dest->payloads[i].asterisk_format, &dest->payloads[i].format, dest->payloads[i].rtp_code);
    
    Joshua Colp's avatar
    Joshua Colp committed
    			}
    		}
    	}
    }
    
    void ast_rtp_codecs_payloads_set_m_type(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, int payload)
    {
    
    	if (payload < 0 || payload >= AST_RTP_MAX_PT || (!static_RTP_PT[payload].rtp_code && !static_RTP_PT[payload].asterisk_format)) {
    
    		ast_rwlock_unlock(&static_RTP_PT_lock);
    
    Joshua Colp's avatar
    Joshua Colp committed
    		return;
    	}
    
    	codecs->payloads[payload].asterisk_format = static_RTP_PT[payload].asterisk_format;
    
    	codecs->payloads[payload].rtp_code = static_RTP_PT[payload].rtp_code;
    	ast_format_copy(&codecs->payloads[payload].format, &static_RTP_PT[payload].format);
    
    Joshua Colp's avatar
    Joshua Colp committed
    
    	ast_debug(1, "Setting payload %d based on m type on %p\n", payload, codecs);
    
    	if (instance && instance->engine && instance->engine->payload_set) {
    
    		instance->engine->payload_set(instance, payload, codecs->payloads[payload].asterisk_format, &codecs->payloads[payload].format, codecs->payloads[payload].rtp_code);
    
    	ast_rwlock_unlock(&static_RTP_PT_lock);
    
    Joshua Colp's avatar
    Joshua Colp committed
    }
    
    int ast_rtp_codecs_payloads_set_rtpmap_type_rate(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, int pt,
    				 char *mimetype, char *mimesubtype,
    				 enum ast_rtp_options options,
    				 unsigned int sample_rate)
    {
    	unsigned int i;
    	int found = 0;
    
    
    	if (pt < 0 || pt >= AST_RTP_MAX_PT)
    
    Joshua Colp's avatar
    Joshua Colp committed
    		return -1; /* bogus payload type */
    
    
    	ast_rwlock_rdlock(&mime_types_lock);
    	for (i = 0; i < mime_types_len; ++i) {
    
    Joshua Colp's avatar
    Joshua Colp committed
    		const struct ast_rtp_mime_type *t = &ast_rtp_mime_types[i];
    
    		if (strcasecmp(mimesubtype, t->subtype)) {
    			continue;
    		}
    
    		if (strcasecmp(mimetype, t->type)) {
    			continue;
    		}
    
    		/* if both sample rates have been supplied, and they don't match,
    
    		 * then this not a match; if one has not been supplied, then the
    		 * rates are not compared */
    
    Joshua Colp's avatar
    Joshua Colp committed
    		if (sample_rate && t->sample_rate &&
    		    (sample_rate != t->sample_rate)) {
    			continue;
    		}
    
    		found = 1;
    		codecs->payloads[pt] = t->payload_type;
    
    
    		if ((t->payload_type.format.id == AST_FORMAT_G726) && t->payload_type.asterisk_format && (options & AST_RTP_OPT_G726_NONSTANDARD)) {
    			ast_format_set(&codecs->payloads[pt].format, AST_FORMAT_G726_AAL2, 0);
    
    Joshua Colp's avatar
    Joshua Colp committed
    		}
    
    		if (instance && instance->engine && instance->engine->payload_set) {
    
    			instance->engine->payload_set(instance, pt, codecs->payloads[i].asterisk_format, &codecs->payloads[i].format, codecs->payloads[i].rtp_code);
    
    Joshua Colp's avatar
    Joshua Colp committed
    
    	return (found ? 0 : -2);
    }
    
    int ast_rtp_codecs_payloads_set_rtpmap_type(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, int payload, char *mimetype, char *mimesubtype, enum ast_rtp_options options)
    {
    	return ast_rtp_codecs_payloads_set_rtpmap_type_rate(codecs, instance, payload, mimetype, mimesubtype, options, 0);
    }
    
    void ast_rtp_codecs_payloads_unset(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, int payload)
    {
    
    	if (payload < 0 || payload >= AST_RTP_MAX_PT) {
    
    Joshua Colp's avatar
    Joshua Colp committed
    		return;
    	}
    
    	ast_debug(2, "Unsetting payload %d on %p\n", payload, codecs);
    
    	codecs->payloads[payload].asterisk_format = 0;
    
    	codecs->payloads[payload].rtp_code = 0;
    	ast_format_clear(&codecs->payloads[payload].format);
    
    Joshua Colp's avatar
    Joshua Colp committed
    
    	if (instance && instance->engine && instance->engine->payload_set) {
    
    		instance->engine->payload_set(instance, payload, 0, NULL, 0);
    
    Joshua Colp's avatar
    Joshua Colp committed
    	}
    }
    
    struct ast_rtp_payload_type ast_rtp_codecs_payload_lookup(struct ast_rtp_codecs *codecs, int payload)
    {
    	struct ast_rtp_payload_type result = { .asterisk_format = 0, };
    
    
    	if (payload < 0 || payload >= AST_RTP_MAX_PT) {
    
    Joshua Colp's avatar
    Joshua Colp committed
    		return result;
    	}
    
    	result.asterisk_format = codecs->payloads[payload].asterisk_format;
    
    	result.rtp_code = codecs->payloads[payload].rtp_code;
    	ast_format_copy(&result.format, &codecs->payloads[payload].format);
    
    	if (!result.rtp_code && !result.asterisk_format) {
    
    		ast_rwlock_rdlock(&static_RTP_PT_lock);
    
    Joshua Colp's avatar
    Joshua Colp committed
    		result = static_RTP_PT[payload];
    
    		ast_rwlock_unlock(&static_RTP_PT_lock);
    
    
    struct ast_format *ast_rtp_codecs_get_payload_format(struct ast_rtp_codecs *codecs, int payload)
    {
    	if (payload < 0 || payload >= AST_RTP_MAX_PT) {
    		return NULL;
    	}
    	if (!codecs->payloads[payload].asterisk_format) {
    		return NULL;
    	}
    	return &codecs->payloads[payload].format;
    }
    
    
    void ast_rtp_codecs_payload_formats(struct ast_rtp_codecs *codecs, struct ast_format_cap *astformats, int *nonastformats)
    
    	ast_format_cap_remove_all(astformats);
    	*nonastformats = 0;
    
    Joshua Colp's avatar
    Joshua Colp committed
    
    	for (i = 0; i < AST_RTP_MAX_PT; i++) {
    
    		if (codecs->payloads[i].rtp_code || codecs->payloads[i].asterisk_format) {
    
    Joshua Colp's avatar
    Joshua Colp committed
    			ast_debug(1, "Incorporating payload %d on %p\n", i, codecs);
    		}
    		if (codecs->payloads[i].asterisk_format) {
    
    			ast_format_cap_add(astformats, &codecs->payloads[i].format);
    
    Joshua Colp's avatar
    Joshua Colp committed
    		} else {
    
    			*nonastformats |= codecs->payloads[i].rtp_code;
    
    int ast_rtp_codecs_payload_code(struct ast_rtp_codecs *codecs, int asterisk_format, const struct ast_format *format, int code)
    
    Joshua Colp's avatar
    Joshua Colp committed
    {
    	int i;
    
    Joshua Colp's avatar
    Joshua Colp committed
    	for (i = 0; i < AST_RTP_MAX_PT; i++) {
    
    		if (codecs->payloads[i].asterisk_format && asterisk_format && format &&
    			(ast_format_cmp(format, &codecs->payloads[i].format) != AST_FORMAT_CMP_NOT_EQUAL)) {
    			return i;
    		} else if (!codecs->payloads[i].asterisk_format && !asterisk_format &&
    			(codecs->payloads[i].rtp_code == code)) {
    
    	ast_rwlock_rdlock(&static_RTP_PT_lock);
    
    Joshua Colp's avatar
    Joshua Colp committed
    	for (i = 0; i < AST_RTP_MAX_PT; i++) {
    
    		if (static_RTP_PT[i].asterisk_format && asterisk_format && format &&
    			(ast_format_cmp(format, &static_RTP_PT[i].format) != AST_FORMAT_CMP_NOT_EQUAL)) {
    
    		} else if (!static_RTP_PT[i].asterisk_format && !asterisk_format &&
    			(static_RTP_PT[i].rtp_code == code)) {
    
    	ast_rwlock_unlock(&static_RTP_PT_lock);
    
    const char *ast_rtp_lookup_mime_subtype2(const int asterisk_format, struct ast_format *format, int code, enum ast_rtp_options options)
    
    Joshua Colp's avatar
    Joshua Colp committed
    {
    	int i;
    
    	ast_rwlock_rdlock(&mime_types_lock);
    	for (i = 0; i < mime_types_len; i++) {
    
    		if (ast_rtp_mime_types[i].payload_type.asterisk_format && asterisk_format && format &&
    			(ast_format_cmp(format, &ast_rtp_mime_types[i].payload_type.format) != AST_FORMAT_CMP_NOT_EQUAL)) {
    			if ((format->id == AST_FORMAT_G726_AAL2) && (options & AST_RTP_OPT_G726_NONSTANDARD)) {
    
    Joshua Colp's avatar
    Joshua Colp committed
    			} else {
    
    				res = ast_rtp_mime_types[i].subtype;
    				break;
    
    		} else if (!ast_rtp_mime_types[i].payload_type.asterisk_format && !asterisk_format &&
    			ast_rtp_mime_types[i].payload_type.rtp_code == code) {
    
    
    			res = ast_rtp_mime_types[i].subtype;
    			break;
    
    unsigned int ast_rtp_lookup_sample_rate2(int asterisk_format, struct ast_format *format, int code)
    
    Joshua Colp's avatar
    Joshua Colp committed
    {
    	unsigned int i;
    
    	ast_rwlock_rdlock(&mime_types_lock);
    	for (i = 0; i < mime_types_len; ++i) {
    
    		if (ast_rtp_mime_types[i].payload_type.asterisk_format && asterisk_format && format &&
    			(ast_format_cmp(format, &ast_rtp_mime_types[i].payload_type.format) != AST_FORMAT_CMP_NOT_EQUAL)) {
    
    			res = ast_rtp_mime_types[i].sample_rate;
    			break;
    
    		} else if (!ast_rtp_mime_types[i].payload_type.asterisk_format && !asterisk_format &&
    			ast_rtp_mime_types[i].payload_type.rtp_code == code) {
    
    			res = ast_rtp_mime_types[i].sample_rate;
    			break;
    
    char *ast_rtp_lookup_mime_multiple2(struct ast_str *buf, struct ast_format_cap *ast_format_capability, int rtp_capability, const int asterisk_format, enum ast_rtp_options options)
    
    Joshua Colp's avatar
    Joshua Colp committed
    	if (!buf) {
    		return NULL;
    	}
    
    
    
    	if (asterisk_format) {
    		struct ast_format tmp_fmt;
    		ast_format_cap_iter_start(ast_format_capability);
    		while (!ast_format_cap_iter_next(ast_format_capability, &tmp_fmt)) {
    			name = ast_rtp_lookup_mime_subtype2(asterisk_format, &tmp_fmt, 0, options);
    
    Joshua Colp's avatar
    Joshua Colp committed
    			ast_str_append(&buf, 0, "%s|", name);
    			found = 1;
    		}
    
    		ast_format_cap_iter_end(ast_format_capability);
    
    	} else {
    		int x;
    		ast_str_append(&buf, 0, "0x%x (", (unsigned int) rtp_capability);
    		for (x = 1; x < AST_RTP_MAX; x <<= 1) {
    			if (rtp_capability & x) {
    				name = ast_rtp_lookup_mime_subtype2(asterisk_format, NULL, x, options);
    				ast_str_append(&buf, 0, "%s|", name);
    				found = 1;
    			}
    		}
    
    Joshua Colp's avatar
    Joshua Colp committed
    	}
    
    	ast_str_append(&buf, 0, "%s", found ? ")" : "nothing)");
    
    	return ast_str_buffer(buf);
    }
    
    void ast_rtp_codecs_packetization_set(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, struct ast_codec_pref *prefs)
    {
    	codecs->pref = *prefs;
    
    	if (instance && instance->engine->packetization_set) {
    		instance->engine->packetization_set(instance, &instance->codecs.pref);
    	}
    }
    
    int ast_rtp_instance_dtmf_begin(struct ast_rtp_instance *instance, char digit)
    {
    	return instance->engine->dtmf_begin ? instance->engine->dtmf_begin(instance, digit) : -1;
    }
    
    int ast_rtp_instance_dtmf_end(struct ast_rtp_instance *instance, char digit)
    {
    	return instance->engine->dtmf_end ? instance->engine->dtmf_end(instance, digit) : -1;
    }
    
    int ast_rtp_instance_dtmf_end_with_duration(struct ast_rtp_instance *instance, char digit, unsigned int duration)
    {
    	return instance->engine->dtmf_end_with_duration ? instance->engine->dtmf_end_with_duration(instance, digit, duration) : -1;
    }
    
    Joshua Colp's avatar
    Joshua Colp committed
    
    int ast_rtp_instance_dtmf_mode_set(struct ast_rtp_instance *instance, enum ast_rtp_dtmf_mode dtmf_mode)
    {
    	if (!instance->engine->dtmf_mode_set || instance->engine->dtmf_mode_set(instance, dtmf_mode)) {
    		return -1;
    	}
    
    	instance->dtmf_mode = dtmf_mode;
    
    	return 0;
    }
    
    enum ast_rtp_dtmf_mode ast_rtp_instance_dtmf_mode_get(struct ast_rtp_instance *instance)
    {
    	return instance->dtmf_mode;
    }
    
    
    void ast_rtp_instance_update_source(struct ast_rtp_instance *instance)
    
    	if (instance->engine->update_source) {
    		instance->engine->update_source(instance);
    	}
    }
    
    void ast_rtp_instance_change_source(struct ast_rtp_instance *instance)
    {
    	if (instance->engine->change_source) {
    		instance->engine->change_source(instance);
    
    Joshua Colp's avatar
    Joshua Colp committed
    	}
    }
    
    int ast_rtp_instance_set_qos(struct ast_rtp_instance *instance, int tos, int cos, const char *desc)
    {
    	return instance->engine->qos ? instance->engine->qos(instance, tos, cos, desc) : -1;
    }
    
    void ast_rtp_instance_stop(struct ast_rtp_instance *instance)
    {
    	if (instance->engine->stop) {
    		instance->engine->stop(instance);
    	}
    }
    
    int ast_rtp_instance_fd(struct ast_rtp_instance *instance, int rtcp)
    {
    	return instance->engine->fd ? instance->engine->fd(instance, rtcp) : -1;
    }
    
    struct ast_rtp_glue *ast_rtp_instance_get_glue(const char *type)
    {
    	struct ast_rtp_glue *glue = NULL;
    
    	AST_RWLIST_RDLOCK(&glues);
    
    	AST_RWLIST_TRAVERSE(&glues, glue, entry) {
    		if (!strcasecmp(glue->type, type)) {
    			break;
    		}
    	}
    
    	AST_RWLIST_UNLOCK(&glues);
    
    	return glue;
    }
    
    static enum ast_bridge_result local_bridge_loop(struct ast_channel *c0, struct ast_channel *c1, struct ast_rtp_instance *instance0, struct ast_rtp_instance *instance1, int timeoutms, int flags, struct ast_frame **fo, struct ast_channel **rc, void *pvt0, void *pvt1)
    {
    	enum ast_bridge_result res = AST_BRIDGE_FAILED;
    	struct ast_channel *who = NULL, *other = NULL, *cs[3] = { NULL, };
    	struct ast_frame *fr = NULL;
    
    	/* Start locally bridging both instances */
    	if (instance0->engine->local_bridge && instance0->engine->local_bridge(instance0, instance1)) {
    		ast_debug(1, "Failed to locally bridge %s to %s, backing out.\n", c0->name, c1->name);
    		ast_channel_unlock(c0);
    		ast_channel_unlock(c1);
    		return AST_BRIDGE_FAILED_NOWARN;
    	}
    	if (instance1->engine->local_bridge && instance1->engine->local_bridge(instance1, instance0)) {
    		ast_debug(1, "Failed to locally bridge %s to %s, backing out.\n", c1->name, c0->name);
    		if (instance0->engine->local_bridge) {
    			instance0->engine->local_bridge(instance0, NULL);
    		}
    		ast_channel_unlock(c0);
    		ast_channel_unlock(c1);
    		return AST_BRIDGE_FAILED_NOWARN;
    	}
    
    	ast_channel_unlock(c0);
    	ast_channel_unlock(c1);
    
    	instance0->bridged = instance1;
    	instance1->bridged = instance0;
    
    	ast_poll_channel_add(c0, c1);
    
    	/* Hop into a loop waiting for a frame from either channel */
    	cs[0] = c0;
    	cs[1] = c1;
    	cs[2] = NULL;
    	for (;;) {
    		/* If the underlying formats have changed force this bridge to break */
    
    		if ((ast_format_cmp(&c0->rawreadformat, &c1->rawwriteformat) == AST_FORMAT_CMP_NOT_EQUAL) ||
    			(ast_format_cmp(&c1->rawreadformat, &c0->rawwriteformat) == AST_FORMAT_CMP_NOT_EQUAL)) {
    
    Joshua Colp's avatar
    Joshua Colp committed
    			ast_debug(1, "rtp-engine-local-bridge: Oooh, formats changed, backing out\n");
    			res = AST_BRIDGE_FAILED_NOWARN;
    			break;
    		}
    		/* Check if anything changed */
    		if ((c0->tech_pvt != pvt0) ||
    		    (c1->tech_pvt != pvt1) ||
    		    (c0->masq || c0->masqr || c1->masq || c1->masqr) ||
    		    (c0->monitor || c0->audiohooks || c1->monitor || c1->audiohooks)) {
    			ast_debug(1, "rtp-engine-local-bridge: Oooh, something is weird, backing out\n");
    			/* If a masquerade needs to happen we have to try to read in a frame so that it actually happens. Without this we risk being called again and going into a loop */
    			if ((c0->masq || c0->masqr) && (fr = ast_read(c0))) {
    				ast_frfree(fr);
    			}
    			if ((c1->masq || c1->masqr) && (fr = ast_read(c1))) {
    				ast_frfree(fr);
    			}
    			res = AST_BRIDGE_RETRY;
    			break;
    		}
    		/* Wait on a channel to feed us a frame */
    		if (!(who = ast_waitfor_n(cs, 2, &timeoutms))) {
    			if (!timeoutms) {
    				res = AST_BRIDGE_RETRY;
    				break;
    			}
    			ast_debug(2, "rtp-engine-local-bridge: Ooh, empty read...\n");
    			if (ast_check_hangup(c0) || ast_check_hangup(c1)) {
    				break;
    			}
    			continue;
    		}
    		/* Read in frame from channel */
    		fr = ast_read(who);
    		other = (who == c0) ? c1 : c0;
    		/* Depending on the frame we may need to break out of our bridge */
    		if (!fr || ((fr->frametype == AST_FRAME_DTMF_BEGIN || fr->frametype == AST_FRAME_DTMF_END) &&
    			    ((who == c0) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) |
    			    ((who == c1) && (flags & AST_BRIDGE_DTMF_CHANNEL_1)))) {
    			/* Record received frame and who */
    			*fo = fr;
    			*rc = who;
    			ast_debug(1, "rtp-engine-local-bridge: Ooh, got a %s\n", fr ? "digit" : "hangup");
    			res = AST_BRIDGE_COMPLETE;
    			break;
    		} else if ((fr->frametype == AST_FRAME_CONTROL) && !(flags & AST_BRIDGE_IGNORE_SIGS)) {
    
    			if ((fr->subclass.integer == AST_CONTROL_HOLD) ||
    			    (fr->subclass.integer == AST_CONTROL_UNHOLD) ||
    			    (fr->subclass.integer == AST_CONTROL_VIDUPDATE) ||
    			    (fr->subclass.integer == AST_CONTROL_SRCUPDATE) ||
    			    (fr->subclass.integer == AST_CONTROL_T38_PARAMETERS)) {
    
    Joshua Colp's avatar
    Joshua Colp committed
    				/* If we are going on hold, then break callback mode and P2P bridging */
    
    				if (fr->subclass.integer == AST_CONTROL_HOLD) {
    
    Joshua Colp's avatar
    Joshua Colp committed
    					if (instance0->engine->local_bridge) {
    						instance0->engine->local_bridge(instance0, NULL);
    					}
    					if (instance1->engine->local_bridge) {
    						instance1->engine->local_bridge(instance1, NULL);
    					}
    					instance0->bridged = NULL;
    					instance1->bridged = NULL;
    
    				} else if (fr->subclass.integer == AST_CONTROL_UNHOLD) {
    
    Joshua Colp's avatar
    Joshua Colp committed
    					if (instance0->engine->local_bridge) {
    						instance0->engine->local_bridge(instance0, instance1);
    					}
    					if (instance1->engine->local_bridge) {
    						instance1->engine->local_bridge(instance1, instance0);
    					}
    					instance0->bridged = instance1;
    					instance1->bridged = instance0;
    				}
    
    				ast_indicate_data(other, fr->subclass.integer, fr->data.ptr, fr->datalen);
    
    Joshua Colp's avatar
    Joshua Colp committed
    				ast_frfree(fr);
    
    			} else if (fr->subclass.integer == AST_CONTROL_CONNECTED_LINE) {
    				if (ast_channel_connected_line_macro(who, other, fr, other == c0, 1)) {
    					ast_indicate_data(other, fr->subclass.integer, fr->data.ptr, fr->datalen);
    				}
    				ast_frfree(fr);
    			} else if (fr->subclass.integer == AST_CONTROL_REDIRECTING) {
    				if (ast_channel_redirecting_macro(who, other, fr, other == c0, 1)) {
    					ast_indicate_data(other, fr->subclass.integer, fr->data.ptr, fr->datalen);
    				}
    				ast_frfree(fr);
    
    Joshua Colp's avatar
    Joshua Colp committed
    			} else {
    				*fo = fr;
    				*rc = who;
    
    				ast_debug(1, "rtp-engine-local-bridge: Got a FRAME_CONTROL (%d) frame on channel %s\n", fr->subclass.integer, who->name);
    
    Joshua Colp's avatar
    Joshua Colp committed
    				res = AST_BRIDGE_COMPLETE;
    				break;
    			}
    		} else {
    			if ((fr->frametype == AST_FRAME_DTMF_BEGIN) ||
    			    (fr->frametype == AST_FRAME_DTMF_END) ||
    			    (fr->frametype == AST_FRAME_VOICE) ||
    			    (fr->frametype == AST_FRAME_VIDEO) ||
    			    (fr->frametype == AST_FRAME_IMAGE) ||
    			    (fr->frametype == AST_FRAME_HTML) ||
    			    (fr->frametype == AST_FRAME_MODEM) ||
    			    (fr->frametype == AST_FRAME_TEXT)) {
    				ast_write(other, fr);
    			}
    
    			ast_frfree(fr);
    		}
    		/* Swap priority */
    		cs[2] = cs[0];
    		cs[0] = cs[1];
    		cs[1] = cs[2];
    	}
    
    	/* Stop locally bridging both instances */
    	if (instance0->engine->local_bridge) {
    		instance0->engine->local_bridge(instance0, NULL);
    	}
    	if (instance1->engine->local_bridge) {
    		instance1->engine->local_bridge(instance1, NULL);
    	}
    
    	instance0->bridged = NULL;
    	instance1->bridged = NULL;
    
    	ast_poll_channel_del(c0, c1);
    
    	return res;
    }
    
    
    static enum ast_bridge_result remote_bridge_loop(struct ast_channel *c0,
    	struct ast_channel *c1,
    	struct ast_rtp_instance *instance0,
    	struct ast_rtp_instance *instance1,
    	struct ast_rtp_instance *vinstance0,
    	struct ast_rtp_instance *vinstance1,
    	struct ast_rtp_instance *tinstance0,
    	struct ast_rtp_instance *tinstance1,
    	struct ast_rtp_glue *glue0,
    	struct ast_rtp_glue *glue1,
    	struct ast_format_cap *cap0,
    	struct ast_format_cap *cap1,
    	int timeoutms,
    	int flags,
    	struct ast_frame **fo,
    	struct ast_channel **rc,
    	void *pvt0,
    	void *pvt1)
    
    Joshua Colp's avatar
    Joshua Colp committed
    {
    	enum ast_bridge_result res = AST_BRIDGE_FAILED;
    	struct ast_channel *who = NULL, *other = NULL, *cs[3] = { NULL, };
    
    	struct ast_format_cap *oldcap0 = ast_format_cap_dup(cap0);
    	struct ast_format_cap *oldcap1 = ast_format_cap_dup(cap1);
    
    Mark Michelson's avatar
    Mark Michelson committed
    	struct ast_sockaddr ac1 = {{0,}}, vac1 = {{0,}}, tac1 = {{0,}}, ac0 = {{0,}}, vac0 = {{0,}}, tac0 = {{0,}};
    	struct ast_sockaddr t1 = {{0,}}, vt1 = {{0,}}, tt1 = {{0,}}, t0 = {{0,}}, vt0 = {{0,}}, tt0 = {{0,}};
    
    Joshua Colp's avatar
    Joshua Colp committed
    	struct ast_frame *fr = NULL;