Skip to content
Snippets Groups Projects
rtp_engine.c 68.7 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>
     */
    
    
    /*** MODULEINFO
    	<support_level>core</support_level>
    
    ***/
    
    /*** DOCUMENTATION
    	<managerEvent language="en_US" name="RTCPSent">
    		<managerEventInstance class="EVENT_FLAG_REPORTING">
    			<synopsis>Raised when an RTCP packet is sent.</synopsis>
    			<syntax>
    
    				<channel_snapshot/>
    
    				<parameter name="SSRC">
    					<para>The SSRC identifier for our stream</para>
    				</parameter>
    				<parameter name="PT">
    					<para>The type of packet for this RTCP report.</para>
    					<enumlist>
    						<enum name="200(SR)"/>
    						<enum name="201(RR)"/>
    					</enumlist>
    				</parameter>
    				<parameter name="To">
    					<para>The address the report is sent to.</para>
    				</parameter>
    				<parameter name="ReportCount">
    					<para>The number of reports that were sent.</para>
    					<para>The report count determines the number of ReportX headers in
    					the message. The X for each set of report headers will range from 0 to
    					<literal>ReportCount - 1</literal>.</para>
    				</parameter>
    				<parameter name="SentNTP" required="false">
    					<para>The time the sender generated the report. Only valid when
    					PT is <literal>200(SR)</literal>.</para>
    				</parameter>
    				<parameter name="SentRTP" required="false">
    					<para>The sender's last RTP timestamp. Only valid when PT is
    					<literal>200(SR)</literal>.</para>
    				</parameter>
    				<parameter name="SentPackets" required="false">
    					<para>The number of packets the sender has sent. Only valid when PT
    					is <literal>200(SR)</literal>.</para>
    				</parameter>
    				<parameter name="SentOctets" required="false">
    					<para>The number of bytes the sender has sent. Only valid when PT is
    					<literal>200(SR)</literal>.</para>
    				</parameter>
    				<parameter name="ReportXSourceSSRC">
    					<para>The SSRC for the source of this report block.</para>
    				</parameter>
    				<parameter name="ReportXFractionLost">
    					<para>The fraction of RTP data packets from <literal>ReportXSourceSSRC</literal>
    					lost since the previous SR or RR report was sent.</para>
    				</parameter>
    				<parameter name="ReportXCumulativeLost">
    					<para>The total number of RTP data packets from <literal>ReportXSourceSSRC</literal>
    					lost since the beginning of reception.</para>
    				</parameter>
    				<parameter name="ReportXHighestSequence">
    					<para>The highest sequence number received in an RTP data packet from
    					<literal>ReportXSourceSSRC</literal>.</para>
    				</parameter>
    				<parameter name="ReportXSequenceNumberCycles">
    					<para>The number of sequence number cycles seen for the RTP data
    					received from <literal>ReportXSourceSSRC</literal>.</para>
    				</parameter>
    				<parameter name="ReportXIAJitter">
    					<para>An estimate of the statistical variance of the RTP data packet
    					interarrival time, measured in timestamp units.</para>
    				</parameter>
    				<parameter name="ReportXLSR">
    					<para>The last SR timestamp received from <literal>ReportXSourceSSRC</literal>.
    					If no SR has been received from <literal>ReportXSourceSSRC</literal>,
    					then 0.</para>
    				</parameter>
    				<parameter name="ReportXDLSR">
    					<para>The delay, expressed in units of 1/65536 seconds, between
    					receiving the last SR packet from <literal>ReportXSourceSSRC</literal>
    					and sending this report.</para>
    				</parameter>
    			</syntax>
    		</managerEventInstance>
    	</managerEvent>
    	<managerEvent language="en_US" name="RTCPReceived">
    		<managerEventInstance class="EVENT_FLAG_REPORTING">
    			<synopsis>Raised when an RTCP packet is received.</synopsis>
    			<syntax>
    
    				<channel_snapshot/>
    
    				<parameter name="SSRC">
    					<para>The SSRC identifier for the remote system</para>
    				</parameter>
    				<xi:include xpointer="xpointer(/docs/managerEvent[@name='RTCPSent']/managerEventInstance/syntax/parameter[@name='PT'])" />
    				<parameter name="From">
    					<para>The address the report was received from.</para>
    				</parameter>
    				<parameter name="RTT">
    					<para>Calculated Round-Trip Time in seconds</para>
    				</parameter>
    				<parameter name="ReportCount">
    					<para>The number of reports that were received.</para>
    					<para>The report count determines the number of ReportX headers in
    					the message. The X for each set of report headers will range from 0 to
    					<literal>ReportCount - 1</literal>.</para>
    				</parameter>
    				<xi:include xpointer="xpointer(/docs/managerEvent[@name='RTCPSent']/managerEventInstance/syntax/parameter[@name='SentNTP'])" />
    				<xi:include xpointer="xpointer(/docs/managerEvent[@name='RTCPSent']/managerEventInstance/syntax/parameter[@name='SentRTP'])" />
    				<xi:include xpointer="xpointer(/docs/managerEvent[@name='RTCPSent']/managerEventInstance/syntax/parameter[@name='SentPackets'])" />
    				<xi:include xpointer="xpointer(/docs/managerEvent[@name='RTCPSent']/managerEventInstance/syntax/parameter[@name='SentOctets'])" />
    				<xi:include xpointer="xpointer(/docs/managerEvent[@name='RTCPSent']/managerEventInstance/syntax/parameter[contains(@name, 'ReportX')])" />
    			</syntax>
    		</managerEventInstance>
    	</managerEvent>
    
    Joshua Colp's avatar
    Joshua Colp committed
    #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"
    
    #include "asterisk/framehook.h"
    
    #include "asterisk/stasis.h"
    #include "asterisk/json.h"
    #include "asterisk/stasis_channels.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;
    
    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;
    
    	/*! Glue currently in use */
    	struct ast_rtp_glue *glue;
    
    	/*! SRTP info associated with the instance */
    	struct ast_srtp *srtp;
    
    	/*! Channel unique ID */
    	char channel_uniqueid[AST_MAX_UNIQUEID];
    
    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;
    
    /*! \brief \ref stasis topic for RTP related messages */
    static struct stasis_topic *rtp_topic;
    
    
    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);
    	}
    
    
    	ast_rtp_codecs_payloads_destroy(&instance->codecs);
    
    
    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);
    
    	if (ast_rtp_codecs_payloads_initialize(&instance->codecs)) {
    		ao2_ref(instance, -1);
    		return NULL;
    	}
    
    
    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;
    }
    
    
    const char *ast_rtp_instance_get_channel_id(struct ast_rtp_instance *instance)
    {
    	return instance->channel_uniqueid;
    }
    
    void ast_rtp_instance_set_channel_id(struct ast_rtp_instance *instance, const char *uniqueid)
    {
    	ast_copy_string(instance->channel_uniqueid, uniqueid, sizeof(instance->channel_uniqueid));
    }
    
    
    Joshua Colp's avatar
    Joshua Colp committed
    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);
    
    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;
    }
    
    
    static int rtp_payload_type_hash(const void *obj, const int flags)
    {
    	const struct ast_rtp_payload_type *type = obj;
    
    	return (flags & OBJ_KEY) ? *payload : type->payload;
    
    }
    
    static int rtp_payload_type_cmp(void *obj, void *arg, int flags)
    {
    	struct ast_rtp_payload_type *type1 = obj, *type2 = arg;
    
    	return (type1->payload == (OBJ_KEY ? *payload : type2->payload)) ? CMP_MATCH | CMP_STOP : 0;
    
    }
    
    int ast_rtp_codecs_payloads_initialize(struct ast_rtp_codecs *codecs)
    {
    	if (!(codecs->payloads = ao2_container_alloc(AST_RTP_MAX_PT, rtp_payload_type_hash, rtp_payload_type_cmp))) {
    		return -1;
    	}
    
    	return 0;
    }
    
    void ast_rtp_codecs_payloads_destroy(struct ast_rtp_codecs *codecs)
    {
    	ao2_cleanup(codecs->payloads);
    }
    
    
    Joshua Colp's avatar
    Joshua Colp committed
    void ast_rtp_codecs_payloads_clear(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance)
    {
    
    	ast_rtp_codecs_payloads_destroy(codecs);
    
    	if (instance && instance->engine && instance->engine->payload_set) {
    		int i;
    		for (i = 0; i < AST_RTP_MAX_PT; i++) {
    
    			instance->engine->payload_set(instance, i, 0, NULL, 0);
    
    
    	ast_rtp_codecs_payloads_initialize(codecs);
    
    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) {
    
    			struct ast_rtp_payload_type *type;
    
    			if (!(type = ao2_alloc(sizeof(*type), NULL))) {
    				/* Unfortunately if this occurs the payloads container will not contain all possible default payloads
    				 * but we err on the side of doing what we can in the hopes that the extreme memory conditions which
    				 * caused this to occur will go away.
    				 */
    				continue;
    			}
    
    
    			type->asterisk_format = static_RTP_PT[i].asterisk_format;
    			type->rtp_code = static_RTP_PT[i].rtp_code;
    			ast_format_copy(&type->format, &static_RTP_PT[i].format);
    
    			ao2_link_flags(codecs->payloads, type, OBJ_NOLOCK);
    
    Joshua Colp's avatar
    Joshua Colp committed
    			if (instance && instance->engine && instance->engine->payload_set) {
    
    				instance->engine->payload_set(instance, i, type->asterisk_format, &type->format, type->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;
    
    Joshua Colp's avatar
    Joshua Colp committed
    
    	for (i = 0; i < AST_RTP_MAX_PT; i++) {
    
    		struct ast_rtp_payload_type *new_type;
    
    		if (!(type = ao2_find(src->payloads, &i, OBJ_KEY | OBJ_NOLOCK))) {
    			continue;
    		}
    
    		if (!(new_type = ao2_alloc(sizeof(*new_type), NULL))) {
    			continue;
    
    
    		ast_debug(2, "Copying payload %d from %p to %p\n", i, src, dest);
    
    
    		*new_type = *type;
    
    		ao2_link_flags(dest->payloads, new_type, OBJ_NOLOCK);
    
    		ao2_ref(new_type, -1);
    
    		if (instance && instance->engine && instance->engine->payload_set) {
    			instance->engine->payload_set(instance, i, type->asterisk_format, &type->format, type->rtp_code);
    		}
    
    		ao2_ref(type, -1);
    
    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) {
    
    		ast_rwlock_unlock(&static_RTP_PT_lock);
    
    	if (!(type = ao2_find(codecs->payloads, &payload, OBJ_KEY | OBJ_NOLOCK))) {
    		if (!(type = ao2_alloc(sizeof(*type), NULL))) {
    			ast_rwlock_unlock(&static_RTP_PT_lock);
    			return;
    		}
    		type->payload = payload;
    		ao2_link_flags(codecs->payloads, type, OBJ_NOLOCK);
    	}
    
    
    	type->asterisk_format = static_RTP_PT[payload].asterisk_format;
    	type->rtp_code = static_RTP_PT[payload].rtp_code;
    
    	ast_format_copy(&type->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, type->asterisk_format, &type->format, type->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];
    
    Joshua Colp's avatar
    Joshua Colp committed
    
    		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;
    
    
    		if (!(type = ao2_find(codecs->payloads, &pt, OBJ_KEY | OBJ_NOLOCK))) {
    			if (!(type = ao2_alloc(sizeof(*type), NULL))) {
    				continue;
    			}
    
    			ao2_link_flags(codecs->payloads, type, OBJ_NOLOCK);
    		}
    
    		*type = 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(&type->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, type->asterisk_format, &type->format, type->rtp_code);
    
    Joshua Colp's avatar
    Joshua Colp committed
    		break;
    	}
    
    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);
    
    
    	ao2_find(codecs->payloads, &payload, OBJ_KEY | OBJ_NOLOCK | OBJ_NODATA | OBJ_UNLINK);
    
    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, }, *type;
    
    	if (payload < 0 || payload >= AST_RTP_MAX_PT) {
    
    Joshua Colp's avatar
    Joshua Colp committed
    		return result;
    	}
    
    
    	if ((type = ao2_find(codecs->payloads, &payload, OBJ_KEY | OBJ_NOLOCK))) {
    		result = *type;
    		ao2_ref(type, -1);
    	}
    
    	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)
    {
    
    	struct ast_rtp_payload_type *type;
    	struct ast_format *format;
    
    
    	if (payload < 0 || payload >= AST_RTP_MAX_PT) {
    		return NULL;
    	}
    
    
    	if (!(type = ao2_find(codecs->payloads, &payload, OBJ_KEY | OBJ_NOLOCK))) {
    
    
    	format = type->asterisk_format ? &type->format : NULL;
    
    	ao2_ref(type, -1);
    
    	return format;
    
    static int rtp_payload_type_add_ast(void *obj, void *arg, int flags)
    
    	struct ast_rtp_payload_type *type = obj;
    	struct ast_format_cap *astformats = arg;
    
    	if (type->asterisk_format) {
    		ast_format_cap_add(astformats, &type->format);
    	}
    
    	return 0;
    }
    
    static int rtp_payload_type_add_nonast(void *obj, void *arg, int flags)
    {
    	struct ast_rtp_payload_type *type = obj;
    	int *nonastformats = arg;
    
    	if (!type->asterisk_format) {
    		*nonastformats |= type->rtp_code;
    	}
    
    	return 0;
    }
    
    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;
    
    	ao2_callback(codecs->payloads, OBJ_NODATA | OBJ_MULTIPLE | OBJ_NOLOCK, rtp_payload_type_add_ast, astformats);
    	ao2_callback(codecs->payloads, OBJ_NODATA | OBJ_MULTIPLE | OBJ_NOLOCK, rtp_payload_type_add_nonast, nonastformats);
    }
    
    static int rtp_payload_type_find_format(void *obj, void *arg, int flags)
    {
    	struct ast_rtp_payload_type *type = obj;
    	struct ast_format *format = arg;
    
    	return (type->asterisk_format && (ast_format_cmp(&type->format, format) != AST_FORMAT_CMP_NOT_EQUAL)) ? CMP_MATCH | CMP_STOP : 0;
    
    static int rtp_payload_type_find_nonast_format(void *obj, void *arg, int flags)
    {
    	struct ast_rtp_payload_type *type = obj;
    	int *rtp_code = arg;
    
    	return ((!type->asterisk_format && (type->rtp_code == *rtp_code)) ? CMP_MATCH | CMP_STOP : 0);
    }
    
    
    int ast_rtp_codecs_payload_code(struct ast_rtp_codecs *codecs, int asterisk_format, const struct ast_format *format, int code)
    
    	struct ast_rtp_payload_type *type;
    	int i, res = -1;
    
    	if (asterisk_format && format && (type = ao2_callback(codecs->payloads, OBJ_NOLOCK, rtp_payload_type_find_format, (void*)format))) {
    
    	} else if (!asterisk_format && (type = ao2_callback(codecs->payloads, OBJ_NOLOCK, rtp_payload_type_find_nonast_format, (void*)&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);
    
    int ast_rtp_codecs_find_payload_code(struct ast_rtp_codecs *codecs, int code)
    {
    	struct ast_rtp_payload_type *type;
    	int res = -1;
    
    	/* Search the payload type in the codecs passed */
    	if ((type = ao2_find(codecs->payloads, &code, OBJ_NOLOCK | OBJ_KEY)))
    	{
    		res = type->payload;
    		ao2_ref(type, -1);
    		return res;
    	}
    
    	return res;
    }
    
    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)