2009-04-02 17:20:52 +00:00
/*
* 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"
2009-06-19 15:41:24 +00:00
#include "asterisk/translate.h"
2009-04-02 17:20:52 +00:00
/*! 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 */
struct sockaddr_in local_address ;
/*! Address that we are sending RTP to */
struct sockaddr_in remote_address ;
2009-06-18 15:20:17 +00:00
/*! Alternate address that we are receiving RTP from */
struct sockaddr_in alt_remote_address ;
2009-04-02 17:20:52 +00:00
/*! 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 ;
/*! DTMF mode in use */
enum ast_rtp_dtmf_mode dtmf_mode ;
2009-08-16 19:27:39 +00:00
/*! Glue currently in use */
struct ast_rtp_glue * glue ;
/*! Channel associated with the instance */
struct ast_channel * chan ;
2009-04-02 17:20:52 +00:00
};
/*! 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. */
static const struct ast_rtp_mime_type {
struct ast_rtp_payload_type payload_type ;
char * type ;
char * subtype ;
unsigned int sample_rate ;
} ast_rtp_mime_types [] = {
{{ 1 , AST_FORMAT_G723_1 }, "audio" , "G723" , 8000 },
{{ 1 , AST_FORMAT_GSM }, "audio" , "GSM" , 8000 },
{{ 1 , AST_FORMAT_ULAW }, "audio" , "PCMU" , 8000 },
{{ 1 , AST_FORMAT_ULAW }, "audio" , "G711U" , 8000 },
{{ 1 , AST_FORMAT_ALAW }, "audio" , "PCMA" , 8000 },
{{ 1 , AST_FORMAT_ALAW }, "audio" , "G711A" , 8000 },
{{ 1 , AST_FORMAT_G726 }, "audio" , "G726-32" , 8000 },
{{ 1 , AST_FORMAT_ADPCM }, "audio" , "DVI4" , 8000 },
{{ 1 , AST_FORMAT_SLINEAR }, "audio" , "L16" , 8000 },
{{ 1 , AST_FORMAT_LPC10 }, "audio" , "LPC" , 8000 },
{{ 1 , AST_FORMAT_G729A }, "audio" , "G729" , 8000 },
{{ 1 , AST_FORMAT_G729A }, "audio" , "G729A" , 8000 },
{{ 1 , AST_FORMAT_G729A }, "audio" , "G.729" , 8000 },
{{ 1 , AST_FORMAT_SPEEX }, "audio" , "speex" , 8000 },
{{ 1 , AST_FORMAT_ILBC }, "audio" , "iLBC" , 8000 },
/* this is the sample rate listed in the RTP profile for the G.722
codec, *NOT* the actual sample rate of the media stream
*/
{{ 1 , AST_FORMAT_G722 }, "audio" , "G722" , 8000 },
{{ 1 , AST_FORMAT_G726_AAL2 }, "audio" , "AAL2-G726-32" , 8000 },
{{ 0 , AST_RTP_DTMF }, "audio" , "telephone-event" , 8000 },
{{ 0 , AST_RTP_CISCO_DTMF }, "audio" , "cisco-telephone-event" , 8000 },
{{ 0 , AST_RTP_CN }, "audio" , "CN" , 8000 },
{{ 1 , AST_FORMAT_JPEG }, "video" , "JPEG" , 90000 },
{{ 1 , AST_FORMAT_PNG }, "video" , "PNG" , 90000 },
{{ 1 , AST_FORMAT_H261 }, "video" , "H261" , 90000 },
{{ 1 , AST_FORMAT_H263 }, "video" , "H263" , 90000 },
{{ 1 , AST_FORMAT_H263_PLUS }, "video" , "h263-1998" , 90000 },
{{ 1 , AST_FORMAT_H264 }, "video" , "H264" , 90000 },
{{ 1 , AST_FORMAT_MP4_VIDEO }, "video" , "MP4V-ES" , 90000 },
{{ 1 , AST_FORMAT_T140RED }, "text" , "RED" , 1000 },
{{ 1 , AST_FORMAT_T140 }, "text" , "T140" , 1000 },
{{ 1 , AST_FORMAT_SIREN7 }, "audio" , "G7221" , 16000 },
{{ 1 , AST_FORMAT_SIREN14 }, "audio" , "G7221" , 32000 },
};
/*!
* \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 const struct ast_rtp_payload_type static_RTP_PT [ AST_RTP_MAX_PT ] = {
[ 0 ] = { 1 , AST_FORMAT_ULAW },
#ifdef USE_DEPRECATED_G726
[ 2 ] = { 1 , AST_FORMAT_G726 }, /* Technically this is G.721, but if Cisco can do it, so can we... */
#endif
[ 3 ] = { 1 , AST_FORMAT_GSM },
[ 4 ] = { 1 , AST_FORMAT_G723_1 },
[ 5 ] = { 1 , AST_FORMAT_ADPCM }, /* 8 kHz */
[ 6 ] = { 1 , AST_FORMAT_ADPCM }, /* 16 kHz */
[ 7 ] = { 1 , AST_FORMAT_LPC10 },
[ 8 ] = { 1 , AST_FORMAT_ALAW },
[ 9 ] = { 1 , AST_FORMAT_G722 },
[ 10 ] = { 1 , AST_FORMAT_SLINEAR }, /* 2 channels */
[ 11 ] = { 1 , AST_FORMAT_SLINEAR }, /* 1 channel */
[ 13 ] = { 0 , AST_RTP_CN },
[ 16 ] = { 1 , AST_FORMAT_ADPCM }, /* 11.025 kHz */
[ 17 ] = { 1 , AST_FORMAT_ADPCM }, /* 22.050 kHz */
[ 18 ] = { 1 , AST_FORMAT_G729A },
[ 19 ] = { 0 , AST_RTP_CN }, /* Also used for CN */
[ 26 ] = { 1 , AST_FORMAT_JPEG },
[ 31 ] = { 1 , AST_FORMAT_H261 },
[ 34 ] = { 1 , AST_FORMAT_H263 },
[ 97 ] = { 1 , AST_FORMAT_ILBC },
[ 98 ] = { 1 , AST_FORMAT_H263_PLUS },
[ 99 ] = { 1 , AST_FORMAT_H264 },
[ 101 ] = { 0 , AST_RTP_DTMF },
[ 102 ] = { 1 , AST_FORMAT_SIREN7 },
[ 103 ] = { 1 , AST_FORMAT_H263_PLUS },
[ 104 ] = { 1 , AST_FORMAT_MP4_VIDEO },
[ 105 ] = { 1 , AST_FORMAT_T140RED }, /* Real time text chat (with redundancy encoding) */
[ 106 ] = { 1 , AST_FORMAT_T140 }, /* Real time text chat */
[ 110 ] = { 1 , AST_FORMAT_SPEEX },
[ 111 ] = { 1 , AST_FORMAT_G726 },
[ 112 ] = { 1 , AST_FORMAT_G726_AAL2 },
[ 115 ] = { 1 , AST_FORMAT_SIREN14 },
[ 121 ] = { 0 , AST_RTP_CISCO_DTMF }, /* Must be type 121 */
};
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 ;
}
/* 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 ;
}
struct ast_rtp_instance * ast_rtp_instance_new ( const char * engine_name , struct sched_context * sched , struct sockaddr_in * sin , void * data )
{
2009-06-02 13:12:59 +00:00
struct sockaddr_in address = { 0 , };
2009-04-02 17:20:52 +00:00
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 ;
2009-04-10 18:14:47 +00:00
instance -> local_address . sin_family = AF_INET ;
instance -> local_address . sin_addr = sin -> sin_addr ;
instance -> remote_address . sin_family = AF_INET ;
2010-01-18 22:03:49 +00:00
address . sin_family = AF_INET ;
2009-06-02 13:12:59 +00:00
address . sin_addr = sin -> sin_addr ;
2009-04-02 17:20:52 +00:00
ast_debug ( 1 , "Using engine '%s' for RTP instance '%p' \n " , engine -> name , instance );
/* And pass it off to the engine to setup */
2009-06-02 13:12:59 +00:00
if ( instance -> engine -> new ( instance , sched , & address , data )) {
2009-04-02 17:20:52 +00:00
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 );
}
int ast_rtp_instance_set_local_address ( struct ast_rtp_instance * instance , struct sockaddr_in * address )
{
2009-04-10 18:14:47 +00:00
instance -> local_address . sin_addr = address -> sin_addr ;
instance -> local_address . sin_port = address -> sin_port ;
2009-04-02 17:20:52 +00:00
return 0 ;
}
int ast_rtp_instance_set_remote_address ( struct ast_rtp_instance * instance , struct sockaddr_in * address )
{
2009-05-27 22:42:15 +00:00
instance -> remote_address . sin_addr = address -> sin_addr ;
instance -> remote_address . sin_port = address -> sin_port ;
2009-04-02 17:20:52 +00:00
/* moo */
if ( instance -> engine -> remote_address_set ) {
2009-04-10 18:14:47 +00:00
instance -> engine -> remote_address_set ( instance , & instance -> remote_address );
2009-04-02 17:20:52 +00:00
}
return 0 ;
}
2009-06-18 15:20:17 +00:00
int ast_rtp_instance_set_alt_remote_address ( struct ast_rtp_instance * instance , struct sockaddr_in * address )
{
instance -> alt_remote_address . sin_addr = address -> sin_addr ;
instance -> alt_remote_address . sin_port = address -> sin_port ;
/* oink */
if ( instance -> engine -> alt_remote_address_set ) {
instance -> engine -> alt_remote_address_set ( instance , & instance -> alt_remote_address );
}
return 0 ;
}
2009-04-02 17:20:52 +00:00
int ast_rtp_instance_get_local_address ( struct ast_rtp_instance * instance , struct sockaddr_in * address )
{
if (( address -> sin_family != AF_INET ) ||
( address -> sin_port != instance -> local_address . sin_port ) ||
( address -> sin_addr . s_addr != instance -> local_address . sin_addr . s_addr )) {
2009-04-06 13:23:12 +00:00
memcpy ( address , & instance -> local_address , sizeof ( * address ));
2009-04-02 17:20:52 +00:00
return 1 ;
}
return 0 ;
}
int ast_rtp_instance_get_remote_address ( struct ast_rtp_instance * instance , struct sockaddr_in * address )
{
if (( address -> sin_family != AF_INET ) ||
( address -> sin_port != instance -> remote_address . sin_port ) ||
( address -> sin_addr . s_addr != instance -> remote_address . sin_addr . s_addr )) {
2009-04-06 13:23:12 +00:00
memcpy ( address , & instance -> remote_address , sizeof ( * address ));
2009-04-02 17:20:52 +00:00
return 1 ;
}
return 0 ;
}
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 ]. code = 0 ;
if ( instance && instance -> engine && instance -> engine -> payload_set ) {
instance -> engine -> payload_set ( instance , i , 0 , 0 );
}
}
}
void ast_rtp_codecs_payloads_default ( struct ast_rtp_codecs * codecs , struct ast_rtp_instance * instance )
{
int i ;
for ( i = 0 ; i < AST_RTP_MAX_PT ; i ++ ) {
if ( static_RTP_PT [ i ]. code ) {
codecs -> payloads [ i ]. asterisk_format = static_RTP_PT [ i ]. asterisk_format ;
codecs -> payloads [ i ]. code = static_RTP_PT [ i ]. code ;
if ( instance && instance -> engine && instance -> engine -> payload_set ) {
instance -> engine -> payload_set ( instance , i , codecs -> payloads [ i ]. asterisk_format , codecs -> payloads [ i ]. code );
}
}
}
}
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 ]. code ) {
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 ]. code = src -> payloads [ i ]. code ;
if ( instance && instance -> engine && instance -> engine -> payload_set ) {
instance -> engine -> payload_set ( instance , i , dest -> payloads [ i ]. asterisk_format , dest -> payloads [ i ]. code );
}
}
}
}
void ast_rtp_codecs_payloads_set_m_type ( struct ast_rtp_codecs * codecs , struct ast_rtp_instance * instance , int payload )
{
2009-10-01 23:59:15 +00:00
if ( payload < 0 || payload >= AST_RTP_MAX_PT || ! static_RTP_PT [ payload ]. code ) {
2009-04-02 17:20:52 +00:00
return ;
}
codecs -> payloads [ payload ]. asterisk_format = static_RTP_PT [ payload ]. asterisk_format ;
codecs -> payloads [ payload ]. code = static_RTP_PT [ payload ]. code ;
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 ]. code );
}
}
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 ;
2009-10-01 23:59:15 +00:00
if ( pt < 0 || pt >= AST_RTP_MAX_PT )
2009-04-02 17:20:52 +00:00
return - 1 ; /* bogus payload type */
for ( i = 0 ; i < ARRAY_LEN ( ast_rtp_mime_types ); ++ i ) {
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 */
if ( sample_rate && t -> sample_rate &&
( sample_rate != t -> sample_rate )) {
continue ;
}
found = 1 ;
codecs -> payloads [ pt ] = t -> payload_type ;
if (( t -> payload_type . code == AST_FORMAT_G726 ) &&
t -> payload_type . asterisk_format &&
( options & AST_RTP_OPT_G726_NONSTANDARD )) {
codecs -> payloads [ pt ]. code = AST_FORMAT_G726_AAL2 ;
}
if ( instance && instance -> engine && instance -> engine -> payload_set ) {
instance -> engine -> payload_set ( instance , pt , codecs -> payloads [ i ]. asterisk_format , codecs -> payloads [ i ]. code );
}
break ;
}
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 )
{
2009-10-01 23:59:15 +00:00
if ( payload < 0 || payload >= AST_RTP_MAX_PT ) {
2009-04-02 17:20:52 +00:00
return ;
}
ast_debug ( 2 , "Unsetting payload %d on %p \n " , payload , codecs );
codecs -> payloads [ payload ]. asterisk_format = 0 ;
codecs -> payloads [ payload ]. code = 0 ;
if ( instance && instance -> engine && instance -> engine -> payload_set ) {
instance -> engine -> payload_set ( instance , payload , 0 , 0 );
}
}
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 , };
2009-10-01 23:59:15 +00:00
if ( payload < 0 || payload >= AST_RTP_MAX_PT ) {
2009-04-02 17:20:52 +00:00
return result ;
}
result . asterisk_format = codecs -> payloads [ payload ]. asterisk_format ;
result . code = codecs -> payloads [ payload ]. code ;
if ( ! result . code ) {
result = static_RTP_PT [ payload ];
}
return result ;
}
2009-11-04 14:05:12 +00:00
void ast_rtp_codecs_payload_formats ( struct ast_rtp_codecs * codecs , format_t * astformats , int * nonastformats )
2009-04-02 17:20:52 +00:00
{
int i ;
* astformats = * nonastformats = 0 ;
for ( i = 0 ; i < AST_RTP_MAX_PT ; i ++ ) {
if ( codecs -> payloads [ i ]. code ) {
ast_debug ( 1 , "Incorporating payload %d on %p \n " , i , codecs );
}
if ( codecs -> payloads [ i ]. asterisk_format ) {
* astformats |= codecs -> payloads [ i ]. code ;
} else {
* nonastformats |= codecs -> payloads [ i ]. code ;
}
}
}
2009-11-04 14:05:12 +00:00
int ast_rtp_codecs_payload_code ( struct ast_rtp_codecs * codecs , const int asterisk_format , const format_t code )
2009-04-02 17:20:52 +00:00
{
int i ;
for ( i = 0 ; i < AST_RTP_MAX_PT ; i ++ ) {
if ( codecs -> payloads [ i ]. asterisk_format == asterisk_format && codecs -> payloads [ i ]. code == code ) {
return i ;
}
}
for ( i = 0 ; i < AST_RTP_MAX_PT ; i ++ ) {
if ( static_RTP_PT [ i ]. asterisk_format == asterisk_format && static_RTP_PT [ i ]. code == code ) {
return i ;
}
}
return - 1 ;
}
2009-11-04 14:05:12 +00:00
const char * ast_rtp_lookup_mime_subtype2 ( const int asterisk_format , const format_t code , enum ast_rtp_options options )
2009-04-02 17:20:52 +00:00
{
int i ;
for ( i = 0 ; i < ARRAY_LEN ( ast_rtp_mime_types ); i ++ ) {
if ( ast_rtp_mime_types [ i ]. payload_type . code == code && ast_rtp_mime_types [ i ]. payload_type . asterisk_format == asterisk_format ) {
if ( asterisk_format && ( code == AST_FORMAT_G726_AAL2 ) && ( options & AST_RTP_OPT_G726_NONSTANDARD )) {
return "G726-32" ;
} else {
return ast_rtp_mime_types [ i ]. subtype ;
}
}
}
return "" ;
}
2009-11-04 14:05:12 +00:00
unsigned int ast_rtp_lookup_sample_rate2 ( int asterisk_format , format_t code )
2009-04-02 17:20:52 +00:00
{
unsigned int i ;
for ( i = 0 ; i < ARRAY_LEN ( ast_rtp_mime_types ); ++ i ) {
if (( ast_rtp_mime_types [ i ]. payload_type . code == code ) && ( ast_rtp_mime_types [ i ]. payload_type . asterisk_format == asterisk_format )) {
return ast_rtp_mime_types [ i ]. sample_rate ;
}
}
return 0 ;
}
2009-11-04 14:05:12 +00:00
char * ast_rtp_lookup_mime_multiple2 ( struct ast_str * buf , const format_t capability , const int asterisk_format , enum ast_rtp_options options )
2009-04-02 17:20:52 +00:00
{
2009-11-04 14:05:12 +00:00
format_t format ;
int found = 0 ;
2009-04-02 17:20:52 +00:00
if ( ! buf ) {
return NULL ;
}
2009-11-04 14:05:12 +00:00
ast_str_append ( & buf , 0 , "0x%llx (" , ( unsigned long long ) capability );
2009-04-02 17:20:52 +00:00
for ( format = 1 ; format < AST_RTP_MAX ; format <<= 1 ) {
if ( capability & format ) {
const char * name = ast_rtp_lookup_mime_subtype2 ( asterisk_format , format , options );
ast_str_append ( & buf , 0 , "%s|" , name );
found = 1 ;
}
}
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_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 ;
}
2010-03-12 22:04:51 +00:00
void ast_rtp_instance_update_source ( struct ast_rtp_instance * instance )
2009-04-02 17:20:52 +00:00
{
2010-03-12 22:04:51 +00:00
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 );
2009-04-02 17:20:52 +00:00
}
}
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 (( c0 -> rawreadformat != c1 -> rawwriteformat ) || ( c1 -> rawreadformat != c0 -> rawwriteformat )) {
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 )) {
2009-11-04 14:05:12 +00:00
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 )) {
2009-04-02 17:20:52 +00:00
/* If we are going on hold, then break callback mode and P2P bridging */
2009-11-04 14:05:12 +00:00
if ( fr -> subclass . integer == AST_CONTROL_HOLD ) {
2009-04-02 17:20:52 +00:00
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 ;
2009-11-04 14:05:12 +00:00
} else if ( fr -> subclass . integer == AST_CONTROL_UNHOLD ) {
2009-04-02 17:20:52 +00:00
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 ;
}
2009-11-04 14:05:12 +00:00
ast_indicate_data ( other , fr -> subclass . integer , fr -> data . ptr , fr -> datalen );
2009-04-02 17:20:52 +00:00
ast_frfree ( fr );
} else {
* fo = fr ;
* rc = who ;
2009-11-04 14:05:12 +00:00
ast_debug ( 1 , "rtp-engine-local-bridge: Got a FRAME_CONTROL (%d) frame on channel %s \n " , fr -> subclass . integer , who -> name );
2009-04-02 17:20:52 +00:00
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 ,
2009-11-04 14:05:12 +00:00
struct ast_rtp_instance * tinstance1 , struct ast_rtp_glue * glue0 , struct ast_rtp_glue * glue1 , format_t codec0 , format_t codec1 , int timeoutms ,
2009-04-02 17:20:52 +00:00
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 , };
2009-11-04 14:05:12 +00:00
format_t oldcodec0 = codec0 , oldcodec1 = codec1 ;
2009-04-02 17:20:52 +00:00
struct sockaddr_in ac1 = { 0 ,}, vac1 = { 0 ,}, tac1 = { 0 ,}, ac0 = { 0 ,}, vac0 = { 0 ,}, tac0 = { 0 ,};
struct sockaddr_in t1 = { 0 ,}, vt1 = { 0 ,}, tt1 = { 0 ,}, t0 = { 0 ,}, vt0 = { 0 ,}, tt0 = { 0 ,};
struct ast_frame * fr = NULL ;
/* Test the first channel */
if ( ! ( glue0 -> update_peer ( c0 , instance1 , vinstance1 , tinstance1 , codec1 , 0 ))) {
ast_rtp_instance_get_remote_address ( instance1 , & ac1 );
if ( vinstance1 ) {
ast_rtp_instance_get_remote_address ( vinstance1 , & vac1 );
}
if ( tinstance1 ) {
ast_rtp_instance_get_remote_address ( tinstance1 , & tac1 );
}
} else {
ast_log ( LOG_WARNING , "Channel '%s' failed to talk to '%s' \n " , c0 -> name , c1 -> name );
}
/* Test the second channel */
if ( ! ( glue1 -> update_peer ( c1 , instance0 , vinstance0 , tinstance0 , codec0 , 0 ))) {
ast_rtp_instance_get_remote_address ( instance0 , & ac0 );
if ( vinstance0 ) {
ast_rtp_instance_get_remote_address ( instance0 , & vac0 );
}
if ( tinstance0 ) {
ast_rtp_instance_get_remote_address ( instance0 , & tac0 );
}
} else {
ast_log ( LOG_WARNING , "Channel '%s' failed to talk to '%s' \n " , c1 -> name , c0 -> name );
}
ast_channel_unlock ( c0 );
ast_channel_unlock ( c1 );
instance0 -> bridged = instance1 ;
instance1 -> bridged = instance0 ;
ast_poll_channel_add ( c0 , c1 );
/* Go into a loop handling any stray frames that may come in */
cs [ 0 ] = c0 ;
cs [ 1 ] = c1 ;
cs [ 2 ] = NULL ;
for (;;) {
/* 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 , "Oooh, something is weird, backing out \n " );
res = AST_BRIDGE_RETRY ;
break ;
}
/* Check if they have changed their address */
ast_rtp_instance_get_remote_address ( instance1 , & t1 );
if ( vinstance1 ) {
ast_rtp_instance_get_remote_address ( vinstance1 , & vt1 );
}
if ( tinstance1 ) {
ast_rtp_instance_get_remote_address ( tinstance1 , & tt1 );
}
if ( glue1 -> get_codec ) {
codec1 = glue1 -> get_codec ( c1 );
}
ast_rtp_instance_get_remote_address ( instance0 , & t0 );
if ( vinstance0 ) {
ast_rtp_instance_get_remote_address ( vinstance0 , & vt0 );
}
if ( tinstance0 ) {
ast_rtp_instance_get_remote_address ( tinstance0 , & tt0 );
}
if ( glue0 -> get_codec ) {
codec0 = glue0 -> get_codec ( c0 );
}
if (( inaddrcmp ( & t1 , & ac1 )) ||
( vinstance1 && inaddrcmp ( & vt1 , & vac1 )) ||
( tinstance1 && inaddrcmp ( & tt1 , & tac1 )) ||
( codec1 != oldcodec1 )) {
2009-11-04 14:05:12 +00:00
ast_debug ( 1 , "Oooh, '%s' changed end address to %s:%d (format %s) \n " ,
c1 -> name , ast_inet_ntoa ( t1 . sin_addr ), ntohs ( t1 . sin_port ), ast_getformatname ( codec1 ));
ast_debug ( 1 , "Oooh, '%s' changed end vaddress to %s:%d (format %s) \n " ,
c1 -> name , ast_inet_ntoa ( vt1 . sin_addr ), ntohs ( vt1 . sin_port ), ast_getformatname ( codec1 ));
ast_debug ( 1 , "Oooh, '%s' changed end taddress to %s:%d (format %s) \n " ,
c1 -> name , ast_inet_ntoa ( tt1 . sin_addr ), ntohs ( tt1 . sin_port ), ast_getformatname ( codec1 ));
ast_debug ( 1 , "Oooh, '%s' was %s:%d/(format %s) \n " ,
c1 -> name , ast_inet_ntoa ( ac1 . sin_addr ), ntohs ( ac1 . sin_port ), ast_getformatname ( oldcodec1 ));
ast_debug ( 1 , "Oooh, '%s' was %s:%d/(format %s) \n " ,
c1 -> name , ast_inet_ntoa ( vac1 . sin_addr ), ntohs ( vac1 . sin_port ), ast_getformatname ( oldcodec1 ));
ast_debug ( 1 , "Oooh, '%s' was %s:%d/(format %s) \n " ,
c1 -> name , ast_inet_ntoa ( tac1 . sin_addr ), ntohs ( tac1 . sin_port ), ast_getformatname ( oldcodec1 ));
2009-04-02 17:20:52 +00:00
if ( glue0 -> update_peer ( c0 , t1 . sin_addr . s_addr ? instance1 : NULL , vt1 . sin_addr . s_addr ? vinstance1 : NULL , tt1 . sin_addr . s_addr ? tinstance1 : NULL , codec1 , 0 )) {
ast_log ( LOG_WARNING , "Channel '%s' failed to update to '%s' \n " , c0 -> name , c1 -> name );
}
memcpy ( & ac1 , & t1 , sizeof ( ac1 ));
memcpy ( & vac1 , & vt1 , sizeof ( vac1 ));
memcpy ( & tac1 , & tt1 , sizeof ( tac1 ));
oldcodec1 = codec1 ;
}
if (( inaddrcmp ( & t0 , & ac0 )) ||
( vinstance0 && inaddrcmp ( & vt0 , & vac0 )) ||
2009-05-18 13:56:16 +00:00
( tinstance0 && inaddrcmp ( & tt0 , & tac0 )) ||
( codec0 != oldcodec0 )) {
2009-11-04 14:05:12 +00:00
ast_debug ( 1 , "Oooh, '%s' changed end address to %s:%d (format %s) \n " ,
c0 -> name , ast_inet_ntoa ( t0 . sin_addr ), ntohs ( t0 . sin_port ), ast_getformatname ( codec0 ));
ast_debug ( 1 , "Oooh, '%s' was %s:%d/(format %s) \n " ,
c0 -> name , ast_inet_ntoa ( ac0 . sin_addr ), ntohs ( ac0 . sin_port ), ast_getformatname ( oldcodec0 ));
2009-04-02 17:20:52 +00:00
if ( glue1 -> update_peer ( c1 , t0 . sin_addr . s_addr ? instance0 : NULL , vt0 . sin_addr . s_addr ? vinstance0 : NULL , tt0 . sin_addr . s_addr ? tinstance0 : NULL , codec0 , 0 )) {
ast_log ( LOG_WARNING , "Channel '%s' failed to update to '%s' \n " , c1 -> name , c0 -> name );
}
memcpy ( & ac0 , & t0 , sizeof ( ac0 ));
memcpy ( & vac0 , & vt0 , sizeof ( vac0 ));
memcpy ( & tac0 , & tt0 , sizeof ( tac0 ));
oldcodec0 = codec0 ;
}
/* Wait for frame to come in on the channels */
if ( ! ( who = ast_waitfor_n ( cs , 2 , & timeoutms ))) {
if ( ! timeoutms ) {
res = AST_BRIDGE_RETRY ;
break ;
}
ast_debug ( 1 , "Ooh, empty read... \n " );
if ( ast_check_hangup ( c0 ) || ast_check_hangup ( c1 )) {
break ;
}
continue ;
}
fr = ast_read ( who );
other = ( who == c0 ) ? c1 : c0 ;
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 ))))) {
/* Break out of bridge */
* fo = fr ;
* rc = who ;
ast_debug ( 1 , "Oooh, got a %s \n " , fr ? "digit" : "hangup" );
res = AST_BRIDGE_COMPLETE ;
break ;
} else if (( fr -> frametype == AST_FRAME_CONTROL ) && ! ( flags & AST_BRIDGE_IGNORE_SIGS )) {
2009-11-04 14:05:12 +00:00
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 )) {
if ( fr -> subclass . integer == AST_CONTROL_HOLD ) {
2009-04-02 17:20:52 +00:00
/* If we someone went on hold we want the other side to reinvite back to us */
if ( who == c0 ) {
glue1 -> update_peer ( c1 , NULL , NULL , NULL , 0 , 0 );
} else {
glue0 -> update_peer ( c0 , NULL , NULL , NULL , 0 , 0 );
}
2009-11-04 14:05:12 +00:00
} else if ( fr -> subclass . integer == AST_CONTROL_UNHOLD ) {
2009-04-02 17:20:52 +00:00
/* If they went off hold they should go back to being direct */
if ( who == c0 ) {
glue1 -> update_peer ( c1 , instance0 , vinstance0 , tinstance0 , codec0 , 0 );
} else {
glue0 -> update_peer ( c0 , instance1 , vinstance1 , tinstance1 , codec1 , 0 );
}
}
/* Update local address information */
ast_rtp_instance_get_remote_address ( instance0 , & t0 );
memcpy ( & ac0 , & t0 , sizeof ( ac0 ));
ast_rtp_instance_get_remote_address ( instance1 , & t1 );
memcpy ( & ac1 , & t1 , sizeof ( ac1 ));
/* Update codec information */
if ( glue0 -> get_codec && c0 -> tech_pvt ) {
oldcodec0 = codec0 = glue0 -> get_codec ( c0 );
}
if ( glue1 -> get_codec && c1 -> tech_pvt ) {
oldcodec1 = codec1 = glue1 -> get_codec ( c1 );
}
2009-11-04 14:05:12 +00:00
ast_indicate_data ( other , fr -> subclass . integer , fr -> data . ptr , fr -> datalen );
2009-04-02 17:20:52 +00:00
ast_frfree ( fr );
} else {
* fo = fr ;
* rc = who ;
2009-11-04 14:05:12 +00:00
ast_debug ( 1 , "Got a FRAME_CONTROL (%d) frame on channel %s \n " , fr -> subclass . integer , who -> name );
2009-04-02 17:20:52 +00:00
return AST_BRIDGE_COMPLETE ;
}
} 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 ];
}
if ( glue0 -> update_peer ( c0 , NULL , NULL , NULL , 0 , 0 )) {
ast_log ( LOG_WARNING , "Channel '%s' failed to break RTP bridge \n " , c0 -> name );
}
if ( glue1 -> update_peer ( c1 , NULL , NULL , NULL , 0 , 0 )) {
ast_log ( LOG_WARNING , "Channel '%s' failed to break RTP bridge \n " , c1 -> name );
}
instance0 -> bridged = NULL ;
instance1 -> bridged = NULL ;
ast_poll_channel_del ( c0 , c1 );
return res ;
}
/*!
* \brief Conditionally unref an rtp instance
*/
static void unref_instance_cond ( struct ast_rtp_instance ** instance )
{
if ( * instance ) {
ao2_ref ( * instance , - 1 );
* instance = NULL ;
}
}
enum ast_bridge_result ast_rtp_instance_bridge ( struct ast_channel * c0 , struct ast_channel * c1 , int flags , struct ast_frame ** fo , struct ast_channel ** rc , int timeoutms )
{
struct ast_rtp_instance * instance0 = NULL , * instance1 = NULL ,
* vinstance0 = NULL , * vinstance1 = NULL ,
* tinstance0 = NULL , * tinstance1 = NULL ;
struct ast_rtp_glue * glue0 , * glue1 ;
enum ast_rtp_glue_result audio_glue0_res = AST_RTP_GLUE_RESULT_FORBID , video_glue0_res = AST_RTP_GLUE_RESULT_FORBID , text_glue0_res = AST_RTP_GLUE_RESULT_FORBID ;
enum ast_rtp_glue_result audio_glue1_res = AST_RTP_GLUE_RESULT_FORBID , video_glue1_res = AST_RTP_GLUE_RESULT_FORBID , text_glue1_res = AST_RTP_GLUE_RESULT_FORBID ;
enum ast_bridge_result res = AST_BRIDGE_FAILED ;
2009-11-04 14:05:12 +00:00
format_t codec0 = 0 , codec1 = 0 ;
2009-04-02 17:20:52 +00:00
int unlock_chans = 1 ;
/* Lock both channels so we can look for the glue that binds them together */
ast_channel_lock ( c0 );
while ( ast_channel_trylock ( c1 )) {
ast_channel_unlock ( c0 );
usleep ( 1 );
ast_channel_lock ( c0 );
}
/* Ensure neither channel got hungup during lock avoidance */
if ( ast_check_hangup ( c0 ) || ast_check_hangup ( c1 )) {
ast_log ( LOG_WARNING , "Got hangup while attempting to bridge '%s' and '%s' \n " , c0 -> name , c1 -> name );
goto done ;
}
/* Grab glue that binds each channel to something using the RTP engine */
if ( ! ( glue0 = ast_rtp_instance_get_glue ( c0 -> tech -> type )) || ! ( glue1 = ast_rtp_instance_get_glue ( c1 -> tech -> type ))) {
2009-04-08 16:27:36 +00:00
ast_debug ( 1 , "Can't find native functions for channel '%s' \n " , glue0 ? c1 -> name : c0 -> name );
2009-04-02 17:20:52 +00:00
goto done ;
}
audio_glue0_res = glue0 -> get_rtp_info ( c0 , & instance0 );
video_glue0_res = glue0 -> get_vrtp_info ? glue0 -> get_vrtp_info ( c0 , & vinstance0 ) : AST_RTP_GLUE_RESULT_FORBID ;
text_glue0_res = glue0 -> get_trtp_info ? glue0 -> get_trtp_info ( c0 , & tinstance0 ) : AST_RTP_GLUE_RESULT_FORBID ;
audio_glue1_res = glue1 -> get_rtp_info ( c1 , & instance1 );
video_glue1_res = glue1 -> get_vrtp_info ? glue1 -> get_vrtp_info ( c1 , & vinstance1 ) : AST_RTP_GLUE_RESULT_FORBID ;
text_glue1_res = glue1 -> get_trtp_info ? glue1 -> get_trtp_info ( c1 , & tinstance1 ) : AST_RTP_GLUE_RESULT_FORBID ;
/* If we are carrying video, and both sides are not going to remotely bridge... fail the native bridge */
if ( video_glue0_res != AST_RTP_GLUE_RESULT_FORBID && ( audio_glue0_res != AST_RTP_GLUE_RESULT_REMOTE || video_glue0_res != AST_RTP_GLUE_RESULT_REMOTE )) {
audio_glue0_res = AST_RTP_GLUE_RESULT_FORBID ;
}
if ( video_glue1_res != AST_RTP_GLUE_RESULT_FORBID && ( audio_glue1_res != AST_RTP_GLUE_RESULT_REMOTE || video_glue1_res != AST_RTP_GLUE_RESULT_REMOTE )) {
audio_glue1_res = AST_RTP_GLUE_RESULT_FORBID ;
}
/* If any sort of bridge is forbidden just completely bail out and go back to generic bridging */
if ( audio_glue0_res == AST_RTP_GLUE_RESULT_FORBID || audio_glue1_res == AST_RTP_GLUE_RESULT_FORBID ) {
res = AST_BRIDGE_FAILED_NOWARN ;
goto done ;
}
2009-04-08 18:12:28 +00:00
/* If we need to get DTMF see if we can do it outside of the RTP stream itself */
if (( flags & AST_BRIDGE_DTMF_CHANNEL_0 ) && instance0 -> properties [ AST_RTP_PROPERTY_DTMF ]) {
res = AST_BRIDGE_FAILED_NOWARN ;
goto done ;
}
if (( flags & AST_BRIDGE_DTMF_CHANNEL_1 ) && instance1 -> properties [ AST_RTP_PROPERTY_DTMF ]) {
res = AST_BRIDGE_FAILED_NOWARN ;
goto done ;
}
2009-04-02 17:20:52 +00:00
/* If we have gotten to a local bridge make sure that both sides have the same local bridge callback and that they are DTMF compatible */
if (( audio_glue0_res == AST_RTP_GLUE_RESULT_LOCAL || audio_glue1_res == AST_RTP_GLUE_RESULT_LOCAL ) && (( instance0 -> engine -> local_bridge != instance1 -> engine -> local_bridge ) || ( instance0 -> engine -> dtmf_compatible && ! instance0 -> engine -> dtmf_compatible ( c0 , instance0 , c1 , instance1 )))) {
res = AST_BRIDGE_FAILED_NOWARN ;
goto done ;
}
/* Make sure that codecs match */
codec0 = glue0 -> get_codec ? glue0 -> get_codec ( c0 ) : 0 ;
codec1 = glue1 -> get_codec ? glue1 -> get_codec ( c1 ) : 0 ;
if ( codec0 && codec1 && ! ( codec0 & codec1 )) {
2009-11-04 14:05:12 +00:00
ast_debug ( 1 , "Channel codec0 = %s is not codec1 = %s, cannot native bridge in RTP. \n " , ast_getformatname ( codec0 ), ast_getformatname ( codec1 ));
2009-04-02 17:20:52 +00:00
res = AST_BRIDGE_FAILED_NOWARN ;
goto done ;
}
2009-08-16 19:27:39 +00:00
instance0 -> glue = glue0 ;
instance1 -> glue = glue1 ;
instance0 -> chan = c0 ;
instance1 -> chan = c1 ;
2009-04-02 17:20:52 +00:00
/* Depending on the end result for bridging either do a local bridge or remote bridge */
if ( audio_glue0_res == AST_RTP_GLUE_RESULT_LOCAL || audio_glue1_res == AST_RTP_GLUE_RESULT_LOCAL ) {
ast_verbose ( VERBOSE_PREFIX_3 "Locally bridging %s and %s \n " , c0 -> name , c1 -> name );
res = local_bridge_loop ( c0 , c1 , instance0 , instance1 , timeoutms , flags , fo , rc , c0 -> tech_pvt , c1 -> tech_pvt );
} else {
ast_verbose ( VERBOSE_PREFIX_3 "Remotely bridging %s and %s \n " , c0 -> name , c1 -> name );
res = remote_bridge_loop ( c0 , c1 , instance0 , instance1 , vinstance0 , vinstance1 ,
tinstance0 , tinstance1 , glue0 , glue1 , codec0 , codec1 , timeoutms , flags ,
fo , rc , c0 -> tech_pvt , c1 -> tech_pvt );
}
2009-08-16 19:27:39 +00:00
instance0 -> glue = NULL ;
instance1 -> glue = NULL ;
instance0 -> chan = NULL ;
instance1 -> chan = NULL ;
2009-04-02 17:20:52 +00:00
unlock_chans = 0 ;
done :
if ( unlock_chans ) {
ast_channel_unlock ( c0 );
ast_channel_unlock ( c1 );
}
unref_instance_cond ( & instance0 );
unref_instance_cond ( & instance1 );
unref_instance_cond ( & vinstance0 );
unref_instance_cond ( & vinstance1 );
unref_instance_cond ( & tinstance0 );
unref_instance_cond ( & tinstance1 );
return res ;
}
struct ast_rtp_instance * ast_rtp_instance_get_bridged ( struct ast_rtp_instance * instance )
{
return instance -> bridged ;
}
void ast_rtp_instance_early_bridge_make_compatible ( struct ast_channel * c0 , struct ast_channel * c1 )
{
struct ast_rtp_instance * instance0 = NULL , * instance1 = NULL ,
* vinstance0 = NULL , * vinstance1 = NULL ,
* tinstance0 = NULL , * tinstance1 = NULL ;
struct ast_rtp_glue * glue0 , * glue1 ;
enum ast_rtp_glue_result audio_glue0_res = AST_RTP_GLUE_RESULT_FORBID , video_glue0_res = AST_RTP_GLUE_RESULT_FORBID , text_glue0_res = AST_RTP_GLUE_RESULT_FORBID ;
enum ast_rtp_glue_result audio_glue1_res = AST_RTP_GLUE_RESULT_FORBID , video_glue1_res = AST_RTP_GLUE_RESULT_FORBID , text_glue1_res = AST_RTP_GLUE_RESULT_FORBID ;
2009-11-04 14:05:12 +00:00
format_t codec0 = 0 , codec1 = 0 ;
2009-04-02 17:20:52 +00:00
int res = 0 ;
/* Lock both channels so we can look for the glue that binds them together */
ast_channel_lock ( c0 );
while ( ast_channel_trylock ( c1 )) {
ast_channel_unlock ( c0 );
usleep ( 1 );
ast_channel_lock ( c0 );
}
/* Grab glue that binds each channel to something using the RTP engine */
if ( ! ( glue0 = ast_rtp_instance_get_glue ( c0 -> tech -> type )) || ! ( glue1 = ast_rtp_instance_get_glue ( c1 -> tech -> type ))) {
2009-04-08 16:27:36 +00:00
ast_debug ( 1 , "Can't find native functions for channel '%s' \n " , glue0 ? c1 -> name : c0 -> name );
2009-04-02 17:20:52 +00:00
goto done ;
}
audio_glue0_res = glue0 -> get_rtp_info ( c0 , & instance0 );
video_glue0_res = glue0 -> get_vrtp_info ? glue0 -> get_vrtp_info ( c0 , & vinstance0 ) : AST_RTP_GLUE_RESULT_FORBID ;
text_glue0_res = glue0 -> get_trtp_info ? glue0 -> get_trtp_info ( c0 , & tinstance0 ) : AST_RTP_GLUE_RESULT_FORBID ;
audio_glue1_res = glue1 -> get_rtp_info ( c1 , & instance1 );
video_glue1_res = glue1 -> get_vrtp_info ? glue1 -> get_vrtp_info ( c1 , & vinstance1 ) : AST_RTP_GLUE_RESULT_FORBID ;
text_glue1_res = glue1 -> get_trtp_info ? glue1 -> get_trtp_info ( c1 , & tinstance1 ) : AST_RTP_GLUE_RESULT_FORBID ;
/* If we are carrying video, and both sides are not going to remotely bridge... fail the native bridge */
if ( video_glue0_res != AST_RTP_GLUE_RESULT_FORBID && ( audio_glue0_res != AST_RTP_GLUE_RESULT_REMOTE || video_glue0_res != AST_RTP_GLUE_RESULT_REMOTE )) {
audio_glue0_res = AST_RTP_GLUE_RESULT_FORBID ;
}
if ( video_glue1_res != AST_RTP_GLUE_RESULT_FORBID && ( audio_glue1_res != AST_RTP_GLUE_RESULT_REMOTE || video_glue1_res != AST_RTP_GLUE_RESULT_REMOTE )) {
audio_glue1_res = AST_RTP_GLUE_RESULT_FORBID ;
}
if ( audio_glue0_res == AST_RTP_GLUE_RESULT_REMOTE && ( video_glue0_res == AST_RTP_GLUE_RESULT_FORBID || video_glue0_res == AST_RTP_GLUE_RESULT_REMOTE ) && glue0 -> get_codec ( c0 )) {
codec0 = glue0 -> get_codec ( c0 );
}
if ( audio_glue1_res == AST_RTP_GLUE_RESULT_REMOTE && ( video_glue1_res == AST_RTP_GLUE_RESULT_FORBID || video_glue1_res == AST_RTP_GLUE_RESULT_REMOTE ) && glue1 -> get_codec ( c1 )) {
codec1 = glue1 -> get_codec ( c1 );
}
/* If any sort of bridge is forbidden just completely bail out and go back to generic bridging */
if ( audio_glue0_res != AST_RTP_GLUE_RESULT_REMOTE || audio_glue1_res != AST_RTP_GLUE_RESULT_REMOTE ) {
goto done ;
}
/* Make sure we have matching codecs */
if ( ! ( codec0 & codec1 )) {
goto done ;
}
ast_rtp_codecs_payloads_copy ( & instance0 -> codecs , & instance1 -> codecs , instance1 );
if ( vinstance0 && vinstance1 ) {
ast_rtp_codecs_payloads_copy ( & vinstance0 -> codecs , & vinstance1 -> codecs , vinstance1 );
}
if ( tinstance0 && tinstance1 ) {
ast_rtp_codecs_payloads_copy ( & tinstance0 -> codecs , & tinstance1 -> codecs , tinstance1 );
}
res = 0 ;
done :
ast_channel_unlock ( c0 );
ast_channel_unlock ( c1 );
unref_instance_cond ( & instance0 );
unref_instance_cond ( & instance1 );
unref_instance_cond ( & vinstance0 );
unref_instance_cond ( & vinstance1 );
unref_instance_cond ( & tinstance0 );
unref_instance_cond ( & tinstance1 );
if ( ! res ) {
ast_debug ( 1 , "Seeded SDP of '%s' with that of '%s' \n " , c0 -> name , c1 ? c1 -> name : "<unspecified>" );
}
}
int ast_rtp_instance_early_bridge ( struct ast_channel * c0 , struct ast_channel * c1 )
{
struct ast_rtp_instance * instance0 = NULL , * instance1 = NULL ,
* vinstance0 = NULL , * vinstance1 = NULL ,
* tinstance0 = NULL , * tinstance1 = NULL ;
struct ast_rtp_glue * glue0 , * glue1 ;
enum ast_rtp_glue_result audio_glue0_res = AST_RTP_GLUE_RESULT_FORBID , video_glue0_res = AST_RTP_GLUE_RESULT_FORBID , text_glue0_res = AST_RTP_GLUE_RESULT_FORBID ;
enum ast_rtp_glue_result audio_glue1_res = AST_RTP_GLUE_RESULT_FORBID , video_glue1_res = AST_RTP_GLUE_RESULT_FORBID , text_glue1_res = AST_RTP_GLUE_RESULT_FORBID ;
2009-11-04 14:05:12 +00:00
format_t codec0 = 0 , codec1 = 0 ;
2009-04-02 17:20:52 +00:00
int res = 0 ;
/* If there is no second channel just immediately bail out, we are of no use in that scenario */
if ( ! c1 ) {
return - 1 ;
}
/* Lock both channels so we can look for the glue that binds them together */
ast_channel_lock ( c0 );
while ( ast_channel_trylock ( c1 )) {
ast_channel_unlock ( c0 );
usleep ( 1 );
ast_channel_lock ( c0 );
}
/* Grab glue that binds each channel to something using the RTP engine */
if ( ! ( glue0 = ast_rtp_instance_get_glue ( c0 -> tech -> type )) || ! ( glue1 = ast_rtp_instance_get_glue ( c1 -> tech -> type ))) {
ast_log ( LOG_WARNING , "Can't find native functions for channel '%s' \n " , glue0 ? c1 -> name : c0 -> name );
goto done ;
}
audio_glue0_res = glue0 -> get_rtp_info ( c0 , & instance0 );
video_glue0_res = glue0 -> get_vrtp_info ? glue0 -> get_vrtp_info ( c0 , & vinstance0 ) : AST_RTP_GLUE_RESULT_FORBID ;
text_glue0_res = glue0 -> get_trtp_info ? glue0 -> get_trtp_info ( c0 , & tinstance0 ) : AST_RTP_GLUE_RESULT_FORBID ;
audio_glue1_res = glue1 -> get_rtp_info ( c1 , & instance1 );
video_glue1_res = glue1 -> get_vrtp_info ? glue1 -> get_vrtp_info ( c1 , & vinstance1 ) : AST_RTP_GLUE_RESULT_FORBID ;
text_glue1_res = glue1 -> get_trtp_info ? glue1 -> get_trtp_info ( c1 , & tinstance1 ) : AST_RTP_GLUE_RESULT_FORBID ;
/* If we are carrying video, and both sides are not going to remotely bridge... fail the native bridge */
if ( video_glue0_res != AST_RTP_GLUE_RESULT_FORBID && ( audio_glue0_res != AST_RTP_GLUE_RESULT_REMOTE || video_glue0_res != AST_RTP_GLUE_RESULT_REMOTE )) {
audio_glue0_res = AST_RTP_GLUE_RESULT_FORBID ;
}
if ( video_glue1_res != AST_RTP_GLUE_RESULT_FORBID && ( audio_glue1_res != AST_RTP_GLUE_RESULT_REMOTE || video_glue1_res != AST_RTP_GLUE_RESULT_REMOTE )) {
audio_glue1_res = AST_RTP_GLUE_RESULT_FORBID ;
}
if ( audio_glue0_res == AST_RTP_GLUE_RESULT_REMOTE && ( video_glue0_res == AST_RTP_GLUE_RESULT_FORBID || video_glue0_res == AST_RTP_GLUE_RESULT_REMOTE ) && glue0 -> get_codec ( c0 )) {
codec0 = glue0 -> get_codec ( c0 );
}
if ( audio_glue1_res == AST_RTP_GLUE_RESULT_REMOTE && ( video_glue1_res == AST_RTP_GLUE_RESULT_FORBID || video_glue1_res == AST_RTP_GLUE_RESULT_REMOTE ) && glue1 -> get_codec ( c1 )) {
codec1 = glue1 -> get_codec ( c1 );
}
/* If any sort of bridge is forbidden just completely bail out and go back to generic bridging */
if ( audio_glue0_res != AST_RTP_GLUE_RESULT_REMOTE || audio_glue1_res != AST_RTP_GLUE_RESULT_REMOTE ) {
goto done ;
}
/* Make sure we have matching codecs */
if ( ! ( codec0 & codec1 )) {
goto done ;
}
/* Bridge media early */
if ( glue0 -> update_peer ( c0 , instance1 , vinstance1 , tinstance1 , codec1 , 0 )) {
ast_log ( LOG_WARNING , "Channel '%s' failed to setup early bridge to '%s' \n " , c0 -> name , c1 ? c1 -> name : "<unspecified>" );
}
res = 0 ;
done :
ast_channel_unlock ( c0 );
ast_channel_unlock ( c1 );
unref_instance_cond ( & instance0 );
unref_instance_cond ( & instance1 );
unref_instance_cond ( & vinstance0 );
unref_instance_cond ( & vinstance1 );
unref_instance_cond ( & tinstance0 );
unref_instance_cond ( & tinstance1 );
if ( ! res ) {
ast_debug ( 1 , "Setting early bridge SDP of '%s' with that of '%s' \n " , c0 -> name , c1 ? c1 -> name : "<unspecified>" );
}
return res ;
}
int ast_rtp_red_init ( struct ast_rtp_instance * instance , int buffer_time , int * payloads , int generations )
{
return instance -> engine -> red_init ? instance -> engine -> red_init ( instance , buffer_time , payloads , generations ) : - 1 ;
}
int ast_rtp_red_buffer ( struct ast_rtp_instance * instance , struct ast_frame * frame )
{
return instance -> engine -> red_buffer ? instance -> engine -> red_buffer ( instance , frame ) : - 1 ;
}
int ast_rtp_instance_get_stats ( struct ast_rtp_instance * instance , struct ast_rtp_instance_stats * stats , enum ast_rtp_instance_stat stat )
{
return instance -> engine -> get_stat ? instance -> engine -> get_stat ( instance , stats , stat ) : - 1 ;
}
char * ast_rtp_instance_get_quality ( struct ast_rtp_instance * instance , enum ast_rtp_instance_stat_field field , char * buf , size_t size )
{
2009-04-10 18:02:44 +00:00
struct ast_rtp_instance_stats stats = { 0 , };
2009-04-02 17:20:52 +00:00
enum ast_rtp_instance_stat stat ;
/* Determine what statistics we will need to retrieve based on field passed in */
if ( field == AST_RTP_INSTANCE_STAT_FIELD_QUALITY ) {
stat = AST_RTP_INSTANCE_STAT_ALL ;
} else if ( field == AST_RTP_INSTANCE_STAT_FIELD_QUALITY_JITTER ) {
stat = AST_RTP_INSTANCE_STAT_COMBINED_JITTER ;
} else if ( field == AST_RTP_INSTANCE_STAT_FIELD_QUALITY_LOSS ) {
stat = AST_RTP_INSTANCE_STAT_COMBINED_LOSS ;
} else if ( field == AST_RTP_INSTANCE_STAT_FIELD_QUALITY_RTT ) {
stat = AST_RTP_INSTANCE_STAT_COMBINED_RTT ;
} else {
return NULL ;
}
/* Attempt to actually retrieve the statistics we need to generate the quality string */
if ( ast_rtp_instance_get_stats ( instance , & stats , stat )) {
return NULL ;
}
/* Now actually fill the buffer with the good information */
if ( field == AST_RTP_INSTANCE_STAT_FIELD_QUALITY ) {
snprintf ( buf , size , "ssrc=%i;themssrc=%u;lp=%u;rxjitter=%u;rxcount=%u;txjitter=%u;txcount=%u;rlp=%u;rtt=%u" ,
stats . local_ssrc , stats . remote_ssrc , stats . rxploss , stats . txjitter , stats . rxcount , stats . rxjitter , stats . txcount , stats . txploss , stats . rtt );
} else if ( field == AST_RTP_INSTANCE_STAT_FIELD_QUALITY_JITTER ) {
snprintf ( buf , size , "minrxjitter=%f;maxrxjitter=%f;avgrxjitter=%f;stdevrxjitter=%f;reported_minjitter=%f;reported_maxjitter=%f;reported_avgjitter=%f;reported_stdevjitter=%f;" ,
stats . local_minjitter , stats . local_maxjitter , stats . local_normdevjitter , sqrt ( stats . local_stdevjitter ), stats . remote_minjitter , stats . remote_maxjitter , stats . remote_normdevjitter , sqrt ( stats . remote_stdevjitter ));
} else if ( field == AST_RTP_INSTANCE_STAT_FIELD_QUALITY_LOSS ) {
snprintf ( buf , size , "minrxlost=%f;maxrxlost=%f;avgrxlost=%f;stdevrxlost=%f;reported_minlost=%f;reported_maxlost=%f;reported_avglost=%f;reported_stdevlost=%f;" ,
stats . local_minrxploss , stats . local_maxrxploss , stats . local_normdevrxploss , sqrt ( stats . local_stdevrxploss ), stats . remote_minrxploss , stats . remote_maxrxploss , stats . remote_normdevrxploss , sqrt ( stats . remote_stdevrxploss ));
} else if ( field == AST_RTP_INSTANCE_STAT_FIELD_QUALITY_RTT ) {
snprintf ( buf , size , "minrtt=%f;maxrtt=%f;avgrtt=%f;stdevrtt=%f;" , stats . minrtt , stats . maxrtt , stats . normdevrtt , stats . stdevrtt );
}
return buf ;
}
void ast_rtp_instance_set_stats_vars ( struct ast_channel * chan , struct ast_rtp_instance * instance )
{
char quality_buf [ AST_MAX_USER_FIELD ], * quality ;
struct ast_channel * bridge = ast_bridged_channel ( chan );
if (( quality = ast_rtp_instance_get_quality ( instance , AST_RTP_INSTANCE_STAT_FIELD_QUALITY , quality_buf , sizeof ( quality_buf )))) {
pbx_builtin_setvar_helper ( chan , "RTPAUDIOQOS" , quality );
if ( bridge ) {
pbx_builtin_setvar_helper ( bridge , "RTPAUDIOQOSBRIDGED" , quality );
}
}
if (( quality = ast_rtp_instance_get_quality ( instance , AST_RTP_INSTANCE_STAT_FIELD_QUALITY_JITTER , quality_buf , sizeof ( quality_buf )))) {
pbx_builtin_setvar_helper ( chan , "RTPAUDIOQOSJITTER" , quality );
if ( bridge ) {
pbx_builtin_setvar_helper ( bridge , "RTPAUDIOQOSJITTERBRIDGED" , quality );
}
}
if (( quality = ast_rtp_instance_get_quality ( instance , AST_RTP_INSTANCE_STAT_FIELD_QUALITY_LOSS , quality_buf , sizeof ( quality_buf )))) {
pbx_builtin_setvar_helper ( chan , "RTPAUDIOQOSLOSS" , quality );
if ( bridge ) {
pbx_builtin_setvar_helper ( bridge , "RTPAUDIOQOSLOSSBRIDGED" , quality );
}
}
if (( quality = ast_rtp_instance_get_quality ( instance , AST_RTP_INSTANCE_STAT_FIELD_QUALITY_RTT , quality_buf , sizeof ( quality_buf )))) {
pbx_builtin_setvar_helper ( chan , "RTPAUDIOQOSRTT" , quality );
if ( bridge ) {
pbx_builtin_setvar_helper ( bridge , "RTPAUDIOQOSRTTBRIDGED" , quality );
}
}
}
2009-11-04 14:05:12 +00:00
int ast_rtp_instance_set_read_format ( struct ast_rtp_instance * instance , format_t format )
2009-04-02 17:20:52 +00:00
{
return instance -> engine -> set_read_format ? instance -> engine -> set_read_format ( instance , format ) : - 1 ;
}
2009-11-04 14:05:12 +00:00
int ast_rtp_instance_set_write_format ( struct ast_rtp_instance * instance , format_t format )
2009-04-02 17:20:52 +00:00
{
return instance -> engine -> set_write_format ? instance -> engine -> set_write_format ( instance , format ) : - 1 ;
}
int ast_rtp_instance_make_compatible ( struct ast_channel * chan , struct ast_rtp_instance * instance , struct ast_channel * peer )
{
struct ast_rtp_glue * glue ;
struct ast_rtp_instance * peer_instance = NULL ;
int res = - 1 ;
if ( ! instance -> engine -> make_compatible ) {
return - 1 ;
}
ast_channel_lock ( peer );
if ( ! ( glue = ast_rtp_instance_get_glue ( peer -> tech -> type ))) {
ast_channel_unlock ( peer );
return - 1 ;
}
glue -> get_rtp_info ( peer , & peer_instance );
if ( ! peer_instance || peer_instance -> engine != instance -> engine ) {
ast_channel_unlock ( peer );
2009-04-29 08:51:21 +00:00
ao2_ref ( peer_instance , - 1 );
peer_instance = NULL ;
2009-04-02 17:20:52 +00:00
return - 1 ;
}
res = instance -> engine -> make_compatible ( chan , instance , peer , peer_instance );
ast_channel_unlock ( peer );
2009-04-29 08:51:21 +00:00
ao2_ref ( peer_instance , - 1 );
peer_instance = NULL ;
2009-04-02 17:20:52 +00:00
return res ;
}
2009-12-01 20:27:37 +00:00
format_t ast_rtp_instance_available_formats ( struct ast_rtp_instance * instance , format_t to_endpoint , format_t to_asterisk )
2009-06-19 15:41:24 +00:00
{
2009-11-04 14:05:12 +00:00
format_t formats ;
2009-06-19 15:41:24 +00:00
if ( instance -> engine -> available_formats && ( formats = instance -> engine -> available_formats ( instance , to_endpoint , to_asterisk ))) {
return formats ;
}
return ast_translate_available_formats ( to_endpoint , to_asterisk );
}
2009-04-02 17:20:52 +00:00
int ast_rtp_instance_activate ( struct ast_rtp_instance * instance )
{
return instance -> engine -> activate ? instance -> engine -> activate ( instance ) : 0 ;
}
void ast_rtp_instance_stun_request ( struct ast_rtp_instance * instance , struct sockaddr_in * suggestion , const char * username )
{
if ( instance -> engine -> stun_request ) {
instance -> engine -> stun_request ( instance , suggestion , username );
}
}
void ast_rtp_instance_set_timeout ( struct ast_rtp_instance * instance , int timeout )
{
instance -> timeout = timeout ;
}
void ast_rtp_instance_set_hold_timeout ( struct ast_rtp_instance * instance , int timeout )
{
instance -> holdtimeout = timeout ;
}
int ast_rtp_instance_get_timeout ( struct ast_rtp_instance * instance )
{
return instance -> timeout ;
}
int ast_rtp_instance_get_hold_timeout ( struct ast_rtp_instance * instance )
{
return instance -> holdtimeout ;
}
2009-08-13 16:44:54 +00:00
struct ast_rtp_engine * ast_rtp_instance_get_engine ( struct ast_rtp_instance * instance )
{
return instance -> engine ;
}
2009-08-16 19:27:39 +00:00
struct ast_rtp_glue * ast_rtp_instance_get_active_glue ( struct ast_rtp_instance * instance )
{
return instance -> glue ;
}
struct ast_channel * ast_rtp_instance_get_chan ( struct ast_rtp_instance * instance )
{
return instance -> chan ;
}