2007-08-08 19:30:52 +00:00
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2007, 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 Audiohooks Architecture
*
2009-05-01 14:58:59 +00:00
* \author Joshua Colp <jcolp@digium.com>
2007-08-08 19:30:52 +00:00
*/
2012-06-15 16:20:16 +00:00
/*** MODULEINFO
<support_level>core</support_level>
***/
2007-08-08 19:30:52 +00:00
# include "asterisk.h"
# include <signal.h>
# include "asterisk/channel.h"
# include "asterisk/utils.h"
# include "asterisk/lock.h"
# include "asterisk/linkedlists.h"
# include "asterisk/audiohook.h"
# include "asterisk/slinfactory.h"
# include "asterisk/frame.h"
# include "asterisk/translate.h"
2014-07-20 22:06:33 +00:00
# include "asterisk/format_cache.h"
2007-08-08 19:30:52 +00:00
2011-02-22 23:04:49 +00:00
# define AST_AUDIOHOOK_SYNC_TOLERANCE 100 /*!< Tolerance in milliseconds for audiohooks synchronization */
# define AST_AUDIOHOOK_SMALL_QUEUE_TOLERANCE 100 /*!< When small queue is enabled, this is the maximum amount of audio that can remain queued at a time. */
2016-04-01 14:50:30 +02:00
# define AST_AUDIOHOOK_LONG_QUEUE_TOLERANCE 500 /*!< Otheriwise we still don't want the queue to grow indefinitely */
2011-02-22 23:04:49 +00:00
2015-05-14 15:21:30 -05:00
# define DEFAULT_INTERNAL_SAMPLE_RATE 8000
2007-08-08 19:30:52 +00:00
struct ast_audiohook_translate {
struct ast_trans_pvt * trans_pvt ;
2014-07-20 22:06:33 +00:00
struct ast_format * format ;
2007-08-08 19:30:52 +00:00
} ;
struct ast_audiohook_list {
2011-02-22 23:04:49 +00:00
/* If all the audiohooks in this list are capable
* of processing slinear at any sample rate, this
* variable will be set and the sample rate will
* be preserved during ast_audiohook_write_list()*/
int native_slin_compatible ;
int list_internal_samp_rate ; /*!< Internal sample rate used when writing to the audiohook list */
2007-08-08 19:30:52 +00:00
struct ast_audiohook_translate in_translate [ 2 ] ;
struct ast_audiohook_translate out_translate [ 2 ] ;
AST_LIST_HEAD_NOLOCK ( , ast_audiohook ) spy_list ;
AST_LIST_HEAD_NOLOCK ( , ast_audiohook ) whisper_list ;
AST_LIST_HEAD_NOLOCK ( , ast_audiohook ) manipulate_list ;
} ;
2011-02-22 23:04:49 +00:00
static int audiohook_set_internal_rate ( struct ast_audiohook * audiohook , int rate , int reset )
{
2014-07-20 22:06:33 +00:00
struct ast_format * slin ;
2011-02-22 23:04:49 +00:00
if ( audiohook - > hook_internal_samp_rate = = rate ) {
return 0 ;
}
audiohook - > hook_internal_samp_rate = rate ;
2014-07-20 22:06:33 +00:00
slin = ast_format_cache_get_slin_by_rate ( rate ) ;
2011-02-22 23:04:49 +00:00
/* Setup the factories that are needed for this audiohook type */
switch ( audiohook - > type ) {
case AST_AUDIOHOOK_TYPE_SPY :
case AST_AUDIOHOOK_TYPE_WHISPER :
if ( reset ) {
2013-11-23 12:40:46 +00:00
ast_slinfactory_destroy ( & audiohook - > read_factory ) ;
2011-02-22 23:04:49 +00:00
ast_slinfactory_destroy ( & audiohook - > write_factory ) ;
}
2014-07-20 22:06:33 +00:00
ast_slinfactory_init_with_format ( & audiohook - > read_factory , slin ) ;
ast_slinfactory_init_with_format ( & audiohook - > write_factory , slin ) ;
2011-02-22 23:04:49 +00:00
break ;
default :
break ;
}
2014-07-20 22:06:33 +00:00
2011-02-22 23:04:49 +00:00
return 0 ;
}
2007-08-08 19:30:52 +00:00
/*! \brief Initialize an audiohook structure
2012-09-22 20:43:30 +00:00
*
2007-08-08 19:30:52 +00:00
* \param audiohook Audiohook structure
2007-09-05 16:31:39 +00:00
* \param type
2012-09-22 20:43:30 +00:00
* \param source, init_flags
*
2007-08-08 19:30:52 +00:00
* \return Returns 0 on success, -1 on failure
*/
2011-02-22 23:04:49 +00:00
int ast_audiohook_init ( struct ast_audiohook * audiohook , enum ast_audiohook_type type , const char * source , enum ast_audiohook_init_flags init_flags )
2007-08-08 19:30:52 +00:00
{
/* Need to keep the type and source */
audiohook - > type = type ;
audiohook - > source = source ;
/* Initialize lock that protects our audiohook */
ast_mutex_init ( & audiohook - > lock ) ;
ast_cond_init ( & audiohook - > trigger , NULL ) ;
2011-02-22 23:04:49 +00:00
audiohook - > init_flags = init_flags ;
/* initialize internal rate at 8khz, this will adjust if necessary */
2015-05-14 15:21:30 -05:00
audiohook_set_internal_rate ( audiohook , DEFAULT_INTERNAL_SAMPLE_RATE , 0 ) ;
2007-08-08 19:30:52 +00:00
/* Since we are just starting out... this audiohook is new */
2009-11-20 17:26:20 +00:00
ast_audiohook_update_status ( audiohook , AST_AUDIOHOOK_STATUS_NEW ) ;
2007-08-08 19:30:52 +00:00
return 0 ;
}
/*! \brief Destroys an audiohook structure
* \param audiohook Audiohook structure
* \return Returns 0 on success, -1 on failure
*/
int ast_audiohook_destroy ( struct ast_audiohook * audiohook )
{
/* Drop the factories used by this audiohook type */
switch ( audiohook - > type ) {
case AST_AUDIOHOOK_TYPE_SPY :
case AST_AUDIOHOOK_TYPE_WHISPER :
2013-11-23 12:40:46 +00:00
ast_slinfactory_destroy ( & audiohook - > read_factory ) ;
2007-08-08 19:30:52 +00:00
ast_slinfactory_destroy ( & audiohook - > write_factory ) ;
break ;
default :
break ;
}
/* Destroy translation path if present */
if ( audiohook - > trans_pvt )
ast_translator_free_path ( audiohook - > trans_pvt ) ;
2014-07-20 22:06:33 +00:00
ao2_cleanup ( audiohook - > format ) ;
2007-08-08 19:30:52 +00:00
/* Lock and trigger be gone! */
ast_cond_destroy ( & audiohook - > trigger ) ;
ast_mutex_destroy ( & audiohook - > lock ) ;
return 0 ;
}
2017-02-01 16:56:50 -05:00
# define SHOULD_MUTE(hook, dir) \
((ast_test_flag(hook, AST_AUDIOHOOK_MUTE_READ) && (dir == AST_AUDIOHOOK_DIRECTION_READ)) || \
(ast_test_flag(hook, AST_AUDIOHOOK_MUTE_WRITE) && (dir == AST_AUDIOHOOK_DIRECTION_WRITE)) || \
(ast_test_flag(hook, AST_AUDIOHOOK_MUTE_READ | AST_AUDIOHOOK_MUTE_WRITE) == (AST_AUDIOHOOK_MUTE_READ | AST_AUDIOHOOK_MUTE_WRITE)))
2007-08-08 19:30:52 +00:00
/*! \brief Writes a frame into the audiohook structure
* \param audiohook Audiohook structure
* \param direction Direction the audio frame came from
* \param frame Frame to write in
* \return Returns 0 on success, -1 on failure
*/
int ast_audiohook_write_frame ( struct ast_audiohook * audiohook , enum ast_audiohook_direction direction , struct ast_frame * frame )
{
struct ast_slinfactory * factory = ( direction = = AST_AUDIOHOOK_DIRECTION_READ ? & audiohook - > read_factory : & audiohook - > write_factory ) ;
2008-04-08 15:05:35 +00:00
struct ast_slinfactory * other_factory = ( direction = = AST_AUDIOHOOK_DIRECTION_READ ? & audiohook - > write_factory : & audiohook - > read_factory ) ;
2008-08-10 19:35:50 +00:00
struct timeval * rwtime = ( direction = = AST_AUDIOHOOK_DIRECTION_READ ? & audiohook - > read_time : & audiohook - > write_time ) , previous_time = * rwtime ;
2009-05-28 14:58:06 +00:00
int our_factory_samples ;
2008-10-14 23:04:44 +00:00
int our_factory_ms ;
int other_factory_samples ;
int other_factory_ms ;
2008-04-08 15:05:35 +00:00
/* Update last feeding time to be current */
2008-08-10 19:35:50 +00:00
* rwtime = ast_tvnow ( ) ;
2008-04-08 15:05:35 +00:00
2009-05-28 14:58:06 +00:00
our_factory_samples = ast_slinfactory_available ( factory ) ;
2011-02-22 23:04:49 +00:00
our_factory_ms = ast_tvdiff_ms ( * rwtime , previous_time ) + ( our_factory_samples / ( audiohook - > hook_internal_samp_rate / 1000 ) ) ;
2008-10-14 23:04:44 +00:00
other_factory_samples = ast_slinfactory_available ( other_factory ) ;
2011-02-22 23:04:49 +00:00
other_factory_ms = other_factory_samples / ( audiohook - > hook_internal_samp_rate / 1000 ) ;
2008-10-14 23:04:44 +00:00
2017-03-15 12:49:12 +08:00
if ( ast_test_flag ( audiohook , AST_AUDIOHOOK_TRIGGER_SYNC ) & & ( our_factory_ms - other_factory_ms > AST_AUDIOHOOK_SYNC_TOLERANCE ) ) {
2011-02-04 16:55:39 +00:00
ast_debug ( 1 , " Flushing audiohook %p so it remains in sync \n " , audiohook ) ;
2008-04-08 15:05:35 +00:00
ast_slinfactory_flush ( factory ) ;
ast_slinfactory_flush ( other_factory ) ;
}
2007-08-08 19:30:52 +00:00
2011-02-22 23:04:49 +00:00
if ( ast_test_flag ( audiohook , AST_AUDIOHOOK_SMALL_QUEUE ) & & ( ( our_factory_ms > AST_AUDIOHOOK_SMALL_QUEUE_TOLERANCE ) | | ( other_factory_ms > AST_AUDIOHOOK_SMALL_QUEUE_TOLERANCE ) ) ) {
2011-02-04 16:55:39 +00:00
ast_debug ( 1 , " Audiohook %p has stale audio in its factories. Flushing them both \n " , audiohook ) ;
2009-05-28 14:58:06 +00:00
ast_slinfactory_flush ( factory ) ;
ast_slinfactory_flush ( other_factory ) ;
2016-04-01 14:50:30 +02:00
} else if ( ( our_factory_ms > AST_AUDIOHOOK_LONG_QUEUE_TOLERANCE ) | | ( other_factory_ms > AST_AUDIOHOOK_LONG_QUEUE_TOLERANCE ) ) {
ast_debug ( 1 , " Audiohook %p has stale audio in its factories. Flushing them both \n " , audiohook ) ;
ast_slinfactory_flush ( factory ) ;
ast_slinfactory_flush ( other_factory ) ;
2009-05-28 14:58:06 +00:00
}
2007-08-08 19:30:52 +00:00
/* Write frame out to respective factory */
ast_slinfactory_feed ( factory , frame ) ;
/* If we need to notify the respective handler of this audiohook, do so */
2008-03-12 18:29:33 +00:00
if ( ( ast_test_flag ( audiohook , AST_AUDIOHOOK_TRIGGER_MODE ) = = AST_AUDIOHOOK_TRIGGER_READ ) & & ( direction = = AST_AUDIOHOOK_DIRECTION_READ ) ) {
ast_cond_signal ( & audiohook - > trigger ) ;
} else if ( ( ast_test_flag ( audiohook , AST_AUDIOHOOK_TRIGGER_MODE ) = = AST_AUDIOHOOK_TRIGGER_WRITE ) & & ( direction = = AST_AUDIOHOOK_DIRECTION_WRITE ) ) {
ast_cond_signal ( & audiohook - > trigger ) ;
} else if ( ast_test_flag ( audiohook , AST_AUDIOHOOK_TRIGGER_SYNC ) ) {
ast_cond_signal ( & audiohook - > trigger ) ;
2007-08-08 19:30:52 +00:00
}
return 0 ;
}
static struct ast_frame * audiohook_read_frame_single ( struct ast_audiohook * audiohook , size_t samples , enum ast_audiohook_direction direction )
{
struct ast_slinfactory * factory = ( direction = = AST_AUDIOHOOK_DIRECTION_READ ? & audiohook - > read_factory : & audiohook - > write_factory ) ;
int vol = ( direction = = AST_AUDIOHOOK_DIRECTION_READ ? audiohook - > options . read_volume : audiohook - > options . write_volume ) ;
short buf [ samples ] ;
struct ast_frame frame = {
. frametype = AST_FRAME_VOICE ,
2014-07-20 22:06:33 +00:00
. subclass . format = ast_format_cache_get_slin_by_rate ( audiohook - > hook_internal_samp_rate ) ,
2008-05-22 16:29:54 +00:00
. data . ptr = buf ,
2007-08-08 19:30:52 +00:00
. datalen = sizeof ( buf ) ,
. samples = samples ,
} ;
/* Ensure the factory is able to give us the samples we want */
2014-04-11 07:07:36 +00:00
if ( samples > ast_slinfactory_available ( factory ) ) {
2007-08-08 19:30:52 +00:00
return NULL ;
2014-04-11 07:07:36 +00:00
}
2012-03-22 19:51:16 +00:00
2007-08-08 19:30:52 +00:00
/* Read data in from factory */
2014-04-11 07:07:36 +00:00
if ( ! ast_slinfactory_read ( factory , buf , samples ) ) {
2007-08-08 19:30:52 +00:00
return NULL ;
2014-04-11 07:07:36 +00:00
}
2007-08-08 19:30:52 +00:00
2017-02-01 16:56:50 -05:00
if ( SHOULD_MUTE ( audiohook , direction ) ) {
/* Swap frame data for zeros if mute is required */
ast_frame_clear ( & frame ) ;
} else if ( vol ) {
/* If a volume adjustment needs to be applied apply it */
2007-08-08 19:30:52 +00:00
ast_frame_adjust_volume ( & frame , vol ) ;
2014-04-11 07:07:36 +00:00
}
2007-08-08 19:30:52 +00:00
return ast_frdup ( & frame ) ;
}
2011-03-11 18:54:45 +00:00
static struct ast_frame * audiohook_read_frame_both ( struct ast_audiohook * audiohook , size_t samples , struct ast_frame * * read_reference , struct ast_frame * * write_reference )
2007-08-08 19:30:52 +00:00
{
2015-08-13 12:30:00 -05:00
int count ;
int usable_read ;
int usable_write ;
short adjust_value ;
short buf1 [ samples ] ;
short buf2 [ samples ] ;
short * read_buf = NULL ;
short * write_buf = NULL ;
2007-08-08 19:30:52 +00:00
struct ast_frame frame = {
. frametype = AST_FRAME_VOICE ,
. datalen = sizeof ( buf1 ) ,
. samples = samples ,
} ;
2008-03-12 18:29:33 +00:00
/* Make sure both factories have the required samples */
usable_read = ( ast_slinfactory_available ( & audiohook - > read_factory ) > = samples ? 1 : 0 ) ;
usable_write = ( ast_slinfactory_available ( & audiohook - > write_factory ) > = samples ? 1 : 0 ) ;
if ( ! usable_read & & ! usable_write ) {
/* If both factories are unusable bail out */
2014-05-09 22:49:26 +00:00
ast_debug ( 1 , " Read factory %p and write factory %p both fail to provide %zu samples \n " , & audiohook - > read_factory , & audiohook - > write_factory , samples ) ;
2008-03-12 18:29:33 +00:00
return NULL ;
}
/* If we want to provide only a read factory make sure we aren't waiting for other audio */
if ( usable_read & & ! usable_write & & ( ast_tvdiff_ms ( ast_tvnow ( ) , audiohook - > write_time ) < ( samples / 8 ) * 2 ) ) {
2008-07-14 10:39:23 +00:00
ast_debug ( 3 , " Write factory %p was pretty quick last time, waiting for them. \n " , & audiohook - > write_factory ) ;
2008-03-12 18:29:33 +00:00
return NULL ;
}
/* If we want to provide only a write factory make sure we aren't waiting for other audio */
2008-07-11 19:14:15 +00:00
if ( usable_write & & ! usable_read & & ( ast_tvdiff_ms ( ast_tvnow ( ) , audiohook - > read_time ) < ( samples / 8 ) * 2 ) ) {
2008-07-14 10:39:23 +00:00
ast_debug ( 3 , " Read factory %p was pretty quick last time, waiting for them. \n " , & audiohook - > read_factory ) ;
2008-03-12 18:29:33 +00:00
return NULL ;
}
2007-08-08 19:30:52 +00:00
/* Start with the read factory... if there are enough samples, read them in */
2008-07-11 20:03:55 +00:00
if ( usable_read ) {
2007-08-21 15:51:49 +00:00
if ( ast_slinfactory_read ( & audiohook - > read_factory , buf1 , samples ) ) {
2007-08-08 19:30:52 +00:00
read_buf = buf1 ;
2017-02-01 16:56:50 -05:00
if ( ( ast_test_flag ( audiohook , AST_AUDIOHOOK_MUTE_READ ) ) ) {
/* Clear the frame data if we are muting */
memset ( buf1 , 0 , sizeof ( buf1 ) ) ;
} else if ( audiohook - > options . read_volume ) {
/* Adjust read volume if need be */
2015-08-13 12:30:00 -05:00
adjust_value = abs ( audiohook - > options . read_volume ) ;
2007-08-21 15:51:49 +00:00
for ( count = 0 ; count < samples ; count + + ) {
2014-04-11 07:07:36 +00:00
if ( audiohook - > options . read_volume > 0 ) {
2007-08-21 15:51:49 +00:00
ast_slinear_saturated_multiply ( & buf1 [ count ] , & adjust_value ) ;
2014-04-11 07:07:36 +00:00
} else if ( audiohook - > options . read_volume < 0 ) {
2007-08-21 15:51:49 +00:00
ast_slinear_saturated_divide ( & buf1 [ count ] , & adjust_value ) ;
2014-04-11 07:07:36 +00:00
}
2007-08-21 15:51:49 +00:00
}
2007-08-08 19:30:52 +00:00
}
}
2012-05-02 02:51:02 +00:00
} else {
ast_debug ( 1 , " Failed to get %d samples from read factory %p \n " , ( int ) samples , & audiohook - > read_factory ) ;
2011-02-04 16:55:39 +00:00
}
2007-08-08 19:30:52 +00:00
/* Move on to the write factory... if there are enough samples, read them in */
2008-07-11 20:03:55 +00:00
if ( usable_write ) {
2007-08-21 15:51:49 +00:00
if ( ast_slinfactory_read ( & audiohook - > write_factory , buf2 , samples ) ) {
2007-08-08 19:30:52 +00:00
write_buf = buf2 ;
2017-02-01 16:56:50 -05:00
if ( ( ast_test_flag ( audiohook , AST_AUDIOHOOK_MUTE_WRITE ) ) ) {
/* Clear the frame data if we are muting */
memset ( buf2 , 0 , sizeof ( buf2 ) ) ;
} else if ( audiohook - > options . write_volume ) {
/* Adjust write volume if need be */
2015-08-13 12:30:00 -05:00
adjust_value = abs ( audiohook - > options . write_volume ) ;
2007-08-21 15:51:49 +00:00
for ( count = 0 ; count < samples ; count + + ) {
2014-04-11 07:07:36 +00:00
if ( audiohook - > options . write_volume > 0 ) {
2007-08-21 15:51:49 +00:00
ast_slinear_saturated_multiply ( & buf2 [ count ] , & adjust_value ) ;
2014-04-11 07:07:36 +00:00
} else if ( audiohook - > options . write_volume < 0 ) {
2007-08-21 15:51:49 +00:00
ast_slinear_saturated_divide ( & buf2 [ count ] , & adjust_value ) ;
2014-04-11 07:07:36 +00:00
}
2007-08-21 15:51:49 +00:00
}
2007-08-08 19:30:52 +00:00
}
}
2012-05-02 02:51:02 +00:00
} else {
ast_debug ( 1 , " Failed to get %d samples from write factory %p \n " , ( int ) samples , & audiohook - > write_factory ) ;
2011-02-04 16:55:39 +00:00
}
2007-08-08 19:30:52 +00:00
2015-08-13 12:22:14 -05:00
frame . subclass . format = ast_format_cache_get_slin_by_rate ( audiohook - > hook_internal_samp_rate ) ;
2019-08-09 16:53:03 -04:00
/* Should we substitute silence if one side lacks audio? */
if ( ( ast_test_flag ( audiohook , AST_AUDIOHOOK_SUBSTITUTE_SILENCE ) ) ) {
if ( read_reference & & ! read_buf & & write_buf ) {
read_buf = buf1 ;
memset ( buf1 , 0 , sizeof ( buf1 ) ) ;
} else if ( write_reference & & read_buf & & ! write_buf ) {
write_buf = buf2 ;
memset ( buf2 , 0 , sizeof ( buf2 ) ) ;
}
}
2007-08-08 19:30:52 +00:00
/* Basically we figure out which buffer to use... and if mixing can be done here */
2011-03-14 13:12:51 +00:00
if ( read_buf & & read_reference ) {
2015-08-13 12:30:00 -05:00
frame . data . ptr = read_buf ;
2011-03-11 18:54:45 +00:00
* read_reference = ast_frdup ( & frame ) ;
}
2011-03-14 13:12:51 +00:00
if ( write_buf & & write_reference ) {
2015-08-13 12:30:00 -05:00
frame . data . ptr = write_buf ;
2011-03-11 18:54:45 +00:00
* write_reference = ast_frdup ( & frame ) ;
}
2015-08-13 12:30:00 -05:00
/* Make the correct buffer part of the built frame, so it gets duplicated. */
if ( read_buf ) {
frame . data . ptr = read_buf ;
if ( write_buf ) {
for ( count = 0 ; count < samples ; count + + ) {
ast_slinear_saturated_add ( read_buf + + , write_buf + + ) ;
}
2011-03-14 13:12:51 +00:00
}
} else if ( write_buf ) {
2015-08-13 12:30:00 -05:00
frame . data . ptr = write_buf ;
2011-03-14 13:12:51 +00:00
} else {
return NULL ;
2011-03-11 18:54:45 +00:00
}
2007-08-08 19:30:52 +00:00
/* Yahoo, a combined copy of the audio! */
return ast_frdup ( & frame ) ;
}
2011-03-11 18:54:45 +00:00
static struct ast_frame * audiohook_read_frame_helper ( struct ast_audiohook * audiohook , size_t samples , enum ast_audiohook_direction direction , struct ast_format * format , struct ast_frame * * read_reference , struct ast_frame * * write_reference )
2007-08-08 19:30:52 +00:00
{
struct ast_frame * read_frame = NULL , * final_frame = NULL ;
2014-07-20 22:06:33 +00:00
struct ast_format * slin ;
2015-03-12 12:58:41 +00:00
2015-05-14 15:21:30 -05:00
/*
* Update the rate if compatibility mode is turned off or if it is
* turned on and the format rate is higher than the current rate.
*
* This makes it so any unnecessary rate switching/resetting does
* not take place and also any associated audiohook_list's internal
* sample rate maintains the highest sample rate between hooks.
*/
if ( ! ast_test_flag ( audiohook , AST_AUDIOHOOK_COMPATIBLE ) | |
( ast_test_flag ( audiohook , AST_AUDIOHOOK_COMPATIBLE ) & &
ast_format_get_sample_rate ( format ) > audiohook - > hook_internal_samp_rate ) ) {
audiohook_set_internal_rate ( audiohook , ast_format_get_sample_rate ( format ) , 1 ) ;
}
2007-08-08 19:30:52 +00:00
2015-07-22 07:16:40 -03:00
/* If the sample rate of the requested format differs from that of the underlying audiohook
* sample rate determine how many samples we actually need to get from the audiohook. This
* needs to occur as the signed linear factory stores them at the rate of the audiohook.
* We do this by determining the duration of audio they've requested and then determining
* how many samples that would be in the audiohook format.
*/
if ( ast_format_get_sample_rate ( format ) ! = audiohook - > hook_internal_samp_rate ) {
samples = ( audiohook - > hook_internal_samp_rate / 1000 ) * ( samples / ( ast_format_get_sample_rate ( format ) / 1000 ) ) ;
}
2012-03-22 19:51:16 +00:00
if ( ! ( read_frame = ( direction = = AST_AUDIOHOOK_DIRECTION_BOTH ?
2015-03-12 12:58:41 +00:00
audiohook_read_frame_both ( audiohook , samples , read_reference , write_reference ) :
audiohook_read_frame_single ( audiohook , samples , direction ) ) ) ) {
2012-03-22 19:51:16 +00:00
return NULL ;
2011-02-22 23:04:49 +00:00
}
2007-08-08 19:30:52 +00:00
2014-07-20 22:06:33 +00:00
slin = ast_format_cache_get_slin_by_rate ( audiohook - > hook_internal_samp_rate ) ;
2007-08-08 19:30:52 +00:00
/* If they don't want signed linear back out, we'll have to send it through the translation path */
2014-07-20 22:06:33 +00:00
if ( ast_format_cmp ( format , slin ) ! = AST_FORMAT_CMP_EQUAL ) {
2007-08-08 19:30:52 +00:00
/* Rebuild translation path if different format then previously */
2014-07-20 22:06:33 +00:00
if ( ast_format_cmp ( format , audiohook - > format ) = = AST_FORMAT_CMP_NOT_EQUAL ) {
2007-08-08 19:30:52 +00:00
if ( audiohook - > trans_pvt ) {
ast_translator_free_path ( audiohook - > trans_pvt ) ;
audiohook - > trans_pvt = NULL ;
}
2011-02-22 23:04:49 +00:00
2007-08-08 19:30:52 +00:00
/* Setup new translation path for this format... if we fail we can't very well return signed linear so free the frame and return nothing */
2014-07-20 22:06:33 +00:00
if ( ! ( audiohook - > trans_pvt = ast_translator_build_path ( format , slin ) ) ) {
2007-08-08 19:30:52 +00:00
ast_frfree ( read_frame ) ;
return NULL ;
}
2014-07-20 22:06:33 +00:00
ao2_replace ( audiohook - > format , format ) ;
2007-08-08 19:30:52 +00:00
}
/* Convert to requested format, and allow the read in frame to be freed */
final_frame = ast_translate ( audiohook - > trans_pvt , read_frame , 1 ) ;
} else {
final_frame = read_frame ;
}
return final_frame ;
}
2011-03-11 18:54:45 +00:00
/*! \brief Reads a frame in from the audiohook structure
* \param audiohook Audiohook structure
* \param samples Number of samples wanted in requested output format
* \param direction Direction the audio frame came from
* \param format Format of frame remote side wants back
* \return Returns frame on success, NULL on failure
*/
struct ast_frame * ast_audiohook_read_frame ( struct ast_audiohook * audiohook , size_t samples , enum ast_audiohook_direction direction , struct ast_format * format )
{
return audiohook_read_frame_helper ( audiohook , samples , direction , format , NULL , NULL ) ;
}
/*! \brief Reads a frame in from the audiohook structure
* \param audiohook Audiohook structure
* \param samples Number of samples wanted
* \param direction Direction the audio frame came from
* \param format Format of frame remote side wants back
* \param read_frame frame pointer for copying read frame data
* \param write_frame frame pointer for copying write frame data
* \return Returns frame on success, NULL on failure
*/
struct ast_frame * ast_audiohook_read_frame_all ( struct ast_audiohook * audiohook , size_t samples , struct ast_format * format , struct ast_frame * * read_frame , struct ast_frame * * write_frame )
{
return audiohook_read_frame_helper ( audiohook , samples , AST_AUDIOHOOK_DIRECTION_BOTH , format , read_frame , write_frame ) ;
}
2011-02-22 23:04:49 +00:00
static void audiohook_list_set_samplerate_compatibility ( struct ast_audiohook_list * audiohook_list )
{
struct ast_audiohook * ah = NULL ;
2015-05-14 15:21:30 -05:00
/*
* Anytime the samplerate compatibility is set (attach/remove an audiohook) the
* list's internal sample rate needs to be reset so that the next time processing
* through write_list, if needed, it will get updated to the correct rate.
*
* A list's internal rate always chooses the higher between its own rate and a
* given rate. If the current rate is being driven by an audiohook that wanted a
* higher rate then when this audiohook is removed the list's rate would remain
* at that level when it should be lower, and with no way to lower it since any
* rate compared against it would be lower.
*
* By setting it back to the lowest rate it can recalulate the new highest rate.
*/
audiohook_list - > list_internal_samp_rate = DEFAULT_INTERNAL_SAMPLE_RATE ;
2011-02-22 23:04:49 +00:00
audiohook_list - > native_slin_compatible = 1 ;
AST_LIST_TRAVERSE ( & audiohook_list - > manipulate_list , ah , list ) {
if ( ! ( ah - > init_flags & AST_AUDIOHOOK_MANIPULATE_ALL_RATES ) ) {
audiohook_list - > native_slin_compatible = 0 ;
return ;
}
}
}
2007-08-08 19:30:52 +00:00
/*! \brief Attach audiohook to channel
* \param chan Channel
* \param audiohook Audiohook structure
* \return Returns 0 on success, -1 on failure
*/
int ast_audiohook_attach ( struct ast_channel * chan , struct ast_audiohook * audiohook )
{
ast_channel_lock ( chan ) ;
2020-03-12 11:22:06 -03:00
/* Don't allow an audiohook to be attached to a channel that is already hung up.
* The hang up process is what actually notifies the audiohook that it should
* stop.
*/
if ( ast_test_flag ( ast_channel_flags ( chan ) , AST_FLAG_ZOMBIE ) ) {
ast_channel_unlock ( chan ) ;
return - 1 ;
}
2012-02-20 23:43:27 +00:00
if ( ! ast_channel_audiohooks ( chan ) ) {
struct ast_audiohook_list * ahlist ;
2007-08-08 19:30:52 +00:00
/* Whoops... allocate a new structure */
2012-02-20 23:43:27 +00:00
if ( ! ( ahlist = ast_calloc ( 1 , sizeof ( * ahlist ) ) ) ) {
2007-08-08 19:30:52 +00:00
ast_channel_unlock ( chan ) ;
return - 1 ;
}
2012-02-20 23:43:27 +00:00
ast_channel_audiohooks_set ( chan , ahlist ) ;
AST_LIST_HEAD_INIT_NOLOCK ( & ast_channel_audiohooks ( chan ) - > spy_list ) ;
AST_LIST_HEAD_INIT_NOLOCK ( & ast_channel_audiohooks ( chan ) - > whisper_list ) ;
AST_LIST_HEAD_INIT_NOLOCK ( & ast_channel_audiohooks ( chan ) - > manipulate_list ) ;
2011-02-22 23:04:49 +00:00
/* This sample rate will adjust as necessary when writing to the list. */
2015-05-14 15:21:30 -05:00
ast_channel_audiohooks ( chan ) - > list_internal_samp_rate = DEFAULT_INTERNAL_SAMPLE_RATE ;
2007-08-08 19:30:52 +00:00
}
/* Drop into respective list */
2014-04-11 07:07:36 +00:00
if ( audiohook - > type = = AST_AUDIOHOOK_TYPE_SPY ) {
2012-02-20 23:43:27 +00:00
AST_LIST_INSERT_TAIL ( & ast_channel_audiohooks ( chan ) - > spy_list , audiohook , list ) ;
2014-04-11 07:07:36 +00:00
} else if ( audiohook - > type = = AST_AUDIOHOOK_TYPE_WHISPER ) {
2012-02-20 23:43:27 +00:00
AST_LIST_INSERT_TAIL ( & ast_channel_audiohooks ( chan ) - > whisper_list , audiohook , list ) ;
2014-04-11 07:07:36 +00:00
} else if ( audiohook - > type = = AST_AUDIOHOOK_TYPE_MANIPULATE ) {
2012-02-20 23:43:27 +00:00
AST_LIST_INSERT_TAIL ( & ast_channel_audiohooks ( chan ) - > manipulate_list , audiohook , list ) ;
2014-04-11 07:07:36 +00:00
}
2007-08-08 19:30:52 +00:00
2015-05-14 15:21:30 -05:00
/*
* Initialize the audiohook's rate to the default. If it needs to be,
* it will get updated later.
*/
audiohook_set_internal_rate ( audiohook , DEFAULT_INTERNAL_SAMPLE_RATE , 1 ) ;
2012-02-20 23:43:27 +00:00
audiohook_list_set_samplerate_compatibility ( ast_channel_audiohooks ( chan ) ) ;
2011-02-22 23:04:49 +00:00
2007-08-08 19:30:52 +00:00
/* Change status over to running since it is now attached */
2009-11-20 17:26:20 +00:00
ast_audiohook_update_status ( audiohook , AST_AUDIOHOOK_STATUS_RUNNING ) ;
2007-08-08 19:30:52 +00:00
2014-10-03 19:42:54 +00:00
if ( ast_channel_is_bridged ( chan ) ) {
ast_channel_set_unbridged_nolock ( chan , 1 ) ;
}
2007-08-08 19:30:52 +00:00
ast_channel_unlock ( chan ) ;
return 0 ;
}
2009-11-20 17:26:20 +00:00
/*! \brief Update audiohook's status
* \param audiohook Audiohook structure
2010-06-08 14:38:18 +00:00
* \param status Audiohook status enum
2010-01-08 19:39:30 +00:00
*
* \note once status is updated to DONE, this function can not be used to set the
* status back to any other setting. Setting DONE effectively locks the status as such.
2009-11-20 17:26:20 +00:00
*/
2010-01-08 19:39:30 +00:00
2009-11-20 17:26:20 +00:00
void ast_audiohook_update_status ( struct ast_audiohook * audiohook , enum ast_audiohook_status status )
{
ast_audiohook_lock ( audiohook ) ;
if ( audiohook - > status ! = AST_AUDIOHOOK_STATUS_DONE ) {
audiohook - > status = status ;
ast_cond_signal ( & audiohook - > trigger ) ;
}
ast_audiohook_unlock ( audiohook ) ;
}
2007-08-08 19:30:52 +00:00
/*! \brief Detach audiohook from channel
* \param audiohook Audiohook structure
* \return Returns 0 on success, -1 on failure
*/
int ast_audiohook_detach ( struct ast_audiohook * audiohook )
{
2014-04-11 07:07:36 +00:00
if ( audiohook - > status = = AST_AUDIOHOOK_STATUS_NEW | | audiohook - > status = = AST_AUDIOHOOK_STATUS_DONE ) {
2007-08-08 19:30:52 +00:00
return 0 ;
2014-04-11 07:07:36 +00:00
}
2007-08-08 19:30:52 +00:00
2009-11-20 17:26:20 +00:00
ast_audiohook_update_status ( audiohook , AST_AUDIOHOOK_STATUS_SHUTDOWN ) ;
2007-08-08 19:30:52 +00:00
2014-04-11 07:07:36 +00:00
while ( audiohook - > status ! = AST_AUDIOHOOK_STATUS_DONE ) {
2007-08-08 19:30:52 +00:00
ast_audiohook_trigger_wait ( audiohook ) ;
2014-04-11 07:07:36 +00:00
}
2007-08-08 19:30:52 +00:00
return 0 ;
}
2013-07-19 23:30:10 +00:00
void ast_audiohook_detach_list ( struct ast_audiohook_list * audiohook_list )
2007-08-08 19:30:52 +00:00
{
2013-07-19 23:30:10 +00:00
int i ;
struct ast_audiohook * audiohook ;
if ( ! audiohook_list ) {
return ;
}
2007-08-08 19:30:52 +00:00
/* Drop any spies */
2007-11-08 05:28:47 +00:00
while ( ( audiohook = AST_LIST_REMOVE_HEAD ( & audiohook_list - > spy_list , list ) ) ) {
2009-11-20 17:26:20 +00:00
ast_audiohook_update_status ( audiohook , AST_AUDIOHOOK_STATUS_DONE ) ;
2007-08-08 19:30:52 +00:00
}
/* Drop any whispering sources */
2007-11-08 05:28:47 +00:00
while ( ( audiohook = AST_LIST_REMOVE_HEAD ( & audiohook_list - > whisper_list , list ) ) ) {
2009-11-20 17:26:20 +00:00
ast_audiohook_update_status ( audiohook , AST_AUDIOHOOK_STATUS_DONE ) ;
2007-08-08 19:30:52 +00:00
}
/* Drop any manipulaters */
2007-11-08 05:28:47 +00:00
while ( ( audiohook = AST_LIST_REMOVE_HEAD ( & audiohook_list - > manipulate_list , list ) ) ) {
2009-11-20 17:26:20 +00:00
ast_audiohook_update_status ( audiohook , AST_AUDIOHOOK_STATUS_DONE ) ;
2007-08-08 19:30:52 +00:00
audiohook - > manipulate_callback ( audiohook , NULL , NULL , 0 ) ;
}
/* Drop translation paths if present */
for ( i = 0 ; i < 2 ; i + + ) {
2014-04-11 07:07:36 +00:00
if ( audiohook_list - > in_translate [ i ] . trans_pvt ) {
2007-08-08 19:30:52 +00:00
ast_translator_free_path ( audiohook_list - > in_translate [ i ] . trans_pvt ) ;
2014-10-30 23:45:25 +00:00
ao2_cleanup ( audiohook_list - > in_translate [ i ] . format ) ;
2014-04-11 07:07:36 +00:00
}
if ( audiohook_list - > out_translate [ i ] . trans_pvt ) {
2007-08-08 19:30:52 +00:00
ast_translator_free_path ( audiohook_list - > out_translate [ i ] . trans_pvt ) ;
2014-10-30 23:45:25 +00:00
ao2_cleanup ( audiohook_list - > in_translate [ i ] . format ) ;
2014-04-11 07:07:36 +00:00
}
2007-08-08 19:30:52 +00:00
}
2012-03-22 19:51:16 +00:00
2007-08-08 19:30:52 +00:00
/* Free ourselves */
ast_free ( audiohook_list ) ;
}
2008-12-19 22:26:16 +00:00
/*! \brief find an audiohook based on its source
* \param audiohook_list The list of audiohooks to search in
* \param source The source of the audiohook we wish to find
* \return Return the corresponding audiohook or NULL if it cannot be found.
*/
2007-08-08 19:30:52 +00:00
static struct ast_audiohook * find_audiohook_by_source ( struct ast_audiohook_list * audiohook_list , const char * source )
{
struct ast_audiohook * audiohook = NULL ;
AST_LIST_TRAVERSE ( & audiohook_list - > spy_list , audiohook , list ) {
2014-04-11 07:07:36 +00:00
if ( ! strcasecmp ( audiohook - > source , source ) ) {
2007-08-08 19:30:52 +00:00
return audiohook ;
2014-04-11 07:07:36 +00:00
}
2007-08-08 19:30:52 +00:00
}
AST_LIST_TRAVERSE ( & audiohook_list - > whisper_list , audiohook , list ) {
2014-04-11 07:07:36 +00:00
if ( ! strcasecmp ( audiohook - > source , source ) ) {
2007-08-08 19:30:52 +00:00
return audiohook ;
2014-04-11 07:07:36 +00:00
}
2007-08-08 19:30:52 +00:00
}
AST_LIST_TRAVERSE ( & audiohook_list - > manipulate_list , audiohook , list ) {
2014-04-11 07:07:36 +00:00
if ( ! strcasecmp ( audiohook - > source , source ) ) {
2007-08-08 19:30:52 +00:00
return audiohook ;
2014-04-11 07:07:36 +00:00
}
2007-08-08 19:30:52 +00:00
}
return NULL ;
}
2014-07-18 16:28:10 +00:00
static void audiohook_move ( struct ast_channel * old_chan , struct ast_channel * new_chan , struct ast_audiohook * audiohook )
2008-12-19 22:26:16 +00:00
{
2010-01-08 19:39:30 +00:00
enum ast_audiohook_status oldstatus ;
2008-12-19 22:26:16 +00:00
/* By locking both channels and the audiohook, we can assure that
* another thread will not have a chance to read the audiohook's status
* as done, even though ast_audiohook_remove signals the trigger
2010-01-08 19:39:30 +00:00
* condition.
2008-12-19 22:26:16 +00:00
*/
ast_audiohook_lock ( audiohook ) ;
2010-01-08 19:39:30 +00:00
oldstatus = audiohook - > status ;
2008-12-19 22:26:16 +00:00
ast_audiohook_remove ( old_chan , audiohook ) ;
ast_audiohook_attach ( new_chan , audiohook ) ;
2010-01-08 19:39:30 +00:00
audiohook - > status = oldstatus ;
2008-12-19 22:26:16 +00:00
ast_audiohook_unlock ( audiohook ) ;
}
2014-07-18 16:28:10 +00:00
void ast_audiohook_move_by_source ( struct ast_channel * old_chan , struct ast_channel * new_chan , const char * source )
{
struct ast_audiohook * audiohook ;
if ( ! ast_channel_audiohooks ( old_chan ) ) {
return ;
}
audiohook = find_audiohook_by_source ( ast_channel_audiohooks ( old_chan ) , source ) ;
if ( ! audiohook ) {
return ;
}
audiohook_move ( old_chan , new_chan , audiohook ) ;
}
void ast_audiohook_move_all ( struct ast_channel * old_chan , struct ast_channel * new_chan )
{
struct ast_audiohook * audiohook ;
struct ast_audiohook_list * audiohook_list ;
audiohook_list = ast_channel_audiohooks ( old_chan ) ;
if ( ! audiohook_list ) {
return ;
}
AST_LIST_TRAVERSE_SAFE_BEGIN ( & audiohook_list - > spy_list , audiohook , list ) {
audiohook_move ( old_chan , new_chan , audiohook ) ;
}
AST_LIST_TRAVERSE_SAFE_END ;
AST_LIST_TRAVERSE_SAFE_BEGIN ( & audiohook_list - > whisper_list , audiohook , list ) {
audiohook_move ( old_chan , new_chan , audiohook ) ;
}
AST_LIST_TRAVERSE_SAFE_END ;
AST_LIST_TRAVERSE_SAFE_BEGIN ( & audiohook_list - > manipulate_list , audiohook , list ) {
audiohook_move ( old_chan , new_chan , audiohook ) ;
}
AST_LIST_TRAVERSE_SAFE_END ;
}
2007-08-08 19:30:52 +00:00
/*! \brief Detach specified source audiohook from channel
* \param chan Channel to detach from
* \param source Name of source to detach
* \return Returns 0 on success, -1 on failure
*/
int ast_audiohook_detach_source ( struct ast_channel * chan , const char * source )
{
struct ast_audiohook * audiohook = NULL ;
ast_channel_lock ( chan ) ;
/* Ensure the channel has audiohooks on it */
2012-02-20 23:43:27 +00:00
if ( ! ast_channel_audiohooks ( chan ) ) {
2007-08-08 19:30:52 +00:00
ast_channel_unlock ( chan ) ;
return - 1 ;
}
2012-02-20 23:43:27 +00:00
audiohook = find_audiohook_by_source ( ast_channel_audiohooks ( chan ) , source ) ;
2007-08-08 19:30:52 +00:00
ast_channel_unlock ( chan ) ;
2014-04-11 07:07:36 +00:00
if ( audiohook & & audiohook - > status ! = AST_AUDIOHOOK_STATUS_DONE ) {
2009-11-20 17:26:20 +00:00
ast_audiohook_update_status ( audiohook , AST_AUDIOHOOK_STATUS_SHUTDOWN ) ;
2014-04-11 07:07:36 +00:00
}
2007-08-08 19:30:52 +00:00
return ( audiohook ? 0 : - 1 ) ;
}
2008-05-01 16:57:19 +00:00
/*!
* \brief Remove an audiohook from a specified channel
*
* \param chan Channel to remove from
* \param audiohook Audiohook to remove
*
* \return Returns 0 on success, -1 on failure
*
* \note The channel does not need to be locked before calling this function
*/
int ast_audiohook_remove ( struct ast_channel * chan , struct ast_audiohook * audiohook )
{
ast_channel_lock ( chan ) ;
2012-02-20 23:43:27 +00:00
if ( ! ast_channel_audiohooks ( chan ) ) {
2008-05-01 16:57:19 +00:00
ast_channel_unlock ( chan ) ;
return - 1 ;
}
2014-04-11 07:07:36 +00:00
if ( audiohook - > type = = AST_AUDIOHOOK_TYPE_SPY ) {
2012-02-20 23:43:27 +00:00
AST_LIST_REMOVE ( & ast_channel_audiohooks ( chan ) - > spy_list , audiohook , list ) ;
2014-04-11 08:28:14 +00:00
} else if ( audiohook - > type = = AST_AUDIOHOOK_TYPE_WHISPER ) {
2012-02-20 23:43:27 +00:00
AST_LIST_REMOVE ( & ast_channel_audiohooks ( chan ) - > whisper_list , audiohook , list ) ;
2014-04-11 08:28:14 +00:00
} else if ( audiohook - > type = = AST_AUDIOHOOK_TYPE_MANIPULATE ) {
2012-02-20 23:43:27 +00:00
AST_LIST_REMOVE ( & ast_channel_audiohooks ( chan ) - > manipulate_list , audiohook , list ) ;
2014-04-11 07:07:36 +00:00
}
2008-05-01 16:57:19 +00:00
2012-02-20 23:43:27 +00:00
audiohook_list_set_samplerate_compatibility ( ast_channel_audiohooks ( chan ) ) ;
2009-11-20 17:26:20 +00:00
ast_audiohook_update_status ( audiohook , AST_AUDIOHOOK_STATUS_DONE ) ;
2008-05-01 16:57:19 +00:00
2014-10-03 19:42:54 +00:00
if ( ast_channel_is_bridged ( chan ) ) {
ast_channel_set_unbridged_nolock ( chan , 1 ) ;
}
2008-05-01 16:57:19 +00:00
ast_channel_unlock ( chan ) ;
return 0 ;
}
2007-08-08 19:30:52 +00:00
/*! \brief Pass a DTMF frame off to be handled by the audiohook core
* \param chan Channel that the list is coming off of
* \param audiohook_list List of audiohooks
* \param direction Direction frame is coming in from
* \param frame The frame itself
* \return Return frame on success, NULL on failure
*/
static struct ast_frame * dtmf_audiohook_write_list ( struct ast_channel * chan , struct ast_audiohook_list * audiohook_list , enum ast_audiohook_direction direction , struct ast_frame * frame )
{
struct ast_audiohook * audiohook = NULL ;
2011-02-22 23:04:49 +00:00
int removed = 0 ;
2007-08-08 19:30:52 +00:00
AST_LIST_TRAVERSE_SAFE_BEGIN ( & audiohook_list - > manipulate_list , audiohook , list ) {
ast_audiohook_lock ( audiohook ) ;
if ( audiohook - > status ! = AST_AUDIOHOOK_STATUS_RUNNING ) {
2007-11-08 05:28:47 +00:00
AST_LIST_REMOVE_CURRENT ( list ) ;
2011-02-22 23:04:49 +00:00
removed = 1 ;
2009-11-20 17:26:20 +00:00
ast_audiohook_update_status ( audiohook , AST_AUDIOHOOK_STATUS_DONE ) ;
2007-08-08 19:30:52 +00:00
ast_audiohook_unlock ( audiohook ) ;
audiohook - > manipulate_callback ( audiohook , NULL , NULL , 0 ) ;
2014-10-03 19:42:54 +00:00
if ( ast_channel_is_bridged ( chan ) ) {
ast_channel_set_unbridged_nolock ( chan , 1 ) ;
}
2007-08-08 19:30:52 +00:00
continue ;
}
2014-04-11 07:07:36 +00:00
if ( ast_test_flag ( audiohook , AST_AUDIOHOOK_WANTS_DTMF ) ) {
2007-08-08 19:30:52 +00:00
audiohook - > manipulate_callback ( audiohook , chan , frame , direction ) ;
2014-04-11 07:07:36 +00:00
}
2007-08-08 19:30:52 +00:00
ast_audiohook_unlock ( audiohook ) ;
}
2007-11-08 05:28:47 +00:00
AST_LIST_TRAVERSE_SAFE_END ;
2007-08-08 19:30:52 +00:00
2011-02-22 23:04:49 +00:00
/* if an audiohook got removed, reset samplerate compatibility */
if ( removed ) {
audiohook_list_set_samplerate_compatibility ( audiohook_list ) ;
}
2007-08-08 19:30:52 +00:00
return frame ;
}
2011-02-22 23:04:49 +00:00
static struct ast_frame * audiohook_list_translate_to_slin ( struct ast_audiohook_list * audiohook_list ,
enum ast_audiohook_direction direction , struct ast_frame * frame )
{
struct ast_audiohook_translate * in_translate = ( direction = = AST_AUDIOHOOK_DIRECTION_READ ?
& audiohook_list - > in_translate [ 0 ] : & audiohook_list - > in_translate [ 1 ] ) ;
struct ast_frame * new_frame = frame ;
2014-07-20 22:06:33 +00:00
struct ast_format * slin ;
2011-02-22 23:04:49 +00:00
2015-05-14 15:21:30 -05:00
/*
* If we are capable of sample rates other that 8khz, update the internal
* audiohook_list's rate and higher sample rate audio arrives. If native
* slin compatibility is turned on all audiohooks in the list will be
* updated as well during read/write processing.
*/
audiohook_list - > list_internal_samp_rate =
MAX ( ast_format_get_sample_rate ( frame - > subclass . format ) , audiohook_list - > list_internal_samp_rate ) ;
2011-02-22 23:04:49 +00:00
2014-07-20 22:06:33 +00:00
slin = ast_format_cache_get_slin_by_rate ( audiohook_list - > list_internal_samp_rate ) ;
if ( ast_format_cmp ( frame - > subclass . format , slin ) = = AST_FORMAT_CMP_EQUAL ) {
2011-02-22 23:04:49 +00:00
return new_frame ;
}
2015-12-02 14:11:08 -06:00
if ( ! in_translate - > format | |
ast_format_cmp ( frame - > subclass . format , in_translate - > format ) ! = AST_FORMAT_CMP_EQUAL ) {
struct ast_trans_pvt * new_trans ;
new_trans = ast_translator_build_path ( slin , frame - > subclass . format ) ;
if ( ! new_trans ) {
return NULL ;
}
2011-02-22 23:04:49 +00:00
if ( in_translate - > trans_pvt ) {
ast_translator_free_path ( in_translate - > trans_pvt ) ;
}
2015-12-02 14:11:08 -06:00
in_translate - > trans_pvt = new_trans ;
2014-07-20 22:06:33 +00:00
ao2_replace ( in_translate - > format , frame - > subclass . format ) ;
2011-02-22 23:04:49 +00:00
}
2014-07-20 22:06:33 +00:00
2011-02-22 23:04:49 +00:00
if ( ! ( new_frame = ast_translate ( in_translate - > trans_pvt , frame , 0 ) ) ) {
return NULL ;
}
return new_frame ;
}
static struct ast_frame * audiohook_list_translate_to_native ( struct ast_audiohook_list * audiohook_list ,
enum ast_audiohook_direction direction , struct ast_frame * slin_frame , struct ast_format * outformat )
{
struct ast_audiohook_translate * out_translate = ( direction = = AST_AUDIOHOOK_DIRECTION_READ ? & audiohook_list - > out_translate [ 0 ] : & audiohook_list - > out_translate [ 1 ] ) ;
struct ast_frame * outframe = NULL ;
2014-07-20 22:06:33 +00:00
if ( ast_format_cmp ( slin_frame - > subclass . format , outformat ) = = AST_FORMAT_CMP_NOT_EQUAL ) {
2011-02-22 23:04:49 +00:00
/* rebuild translators if necessary */
2014-07-20 22:06:33 +00:00
if ( ast_format_cmp ( out_translate - > format , outformat ) = = AST_FORMAT_CMP_NOT_EQUAL ) {
2011-02-22 23:04:49 +00:00
if ( out_translate - > trans_pvt ) {
ast_translator_free_path ( out_translate - > trans_pvt ) ;
}
2014-07-20 22:06:33 +00:00
if ( ! ( out_translate - > trans_pvt = ast_translator_build_path ( outformat , slin_frame - > subclass . format ) ) ) {
2011-02-22 23:04:49 +00:00
return NULL ;
}
2014-07-20 22:06:33 +00:00
ao2_replace ( out_translate - > format , outformat ) ;
2011-02-22 23:04:49 +00:00
}
/* translate back to the format the frame came in as. */
if ( ! ( outframe = ast_translate ( out_translate - > trans_pvt , slin_frame , 0 ) ) ) {
return NULL ;
}
}
return outframe ;
}
2015-05-14 15:21:30 -05:00
/*!
*\brief Set the audiohook's internal sample rate to the audiohook_list's rate,
* but only when native slin compatibility is turned on.
*
* \param audiohook_list audiohook_list data object
* \param audiohook the audiohook to update
* \param rate the current max internal sample rate
*/
static void audiohook_list_set_hook_rate ( struct ast_audiohook_list * audiohook_list ,
struct ast_audiohook * audiohook , int * rate )
{
/* The rate should always be the max between itself and the hook */
if ( audiohook - > hook_internal_samp_rate > * rate ) {
* rate = audiohook - > hook_internal_samp_rate ;
}
/*
* If native slin compatibility is turned on then update the audiohook
* with the audiohook_list's current rate. Note, the audiohook's rate is
* set to the audiohook_list's rate and not the given rate. If there is
* a change in rate the hook's rate is changed on its next check.
*/
if ( audiohook_list - > native_slin_compatible ) {
ast_set_flag ( audiohook , AST_AUDIOHOOK_COMPATIBLE ) ;
audiohook_set_internal_rate ( audiohook , audiohook_list - > list_internal_samp_rate , 1 ) ;
} else {
ast_clear_flag ( audiohook , AST_AUDIOHOOK_COMPATIBLE ) ;
}
}
2010-04-29 15:33:27 +00:00
/*!
* \brief Pass an AUDIO frame off to be handled by the audiohook core
*
* \details
* This function has 3 ast_frames and 3 parts to handle each. At the beginning of this
* function all 3 frames, start_frame, middle_frame, and end_frame point to the initial
* input frame.
*
* Part_1: Translate the start_frame into SLINEAR audio if it is not already in that
* format. The result of this part is middle_frame is guaranteed to be in
* SLINEAR format for Part_2.
* Part_2: Send middle_frame off to spies and manipulators. At this point middle_frame is
* either a new frame as result of the translation, or points directly to the start_frame
2011-02-22 23:04:49 +00:00
* because no translation to SLINEAR audio was required.
* Part_3: Translate end_frame's audio back into the format of start frame if necessary. This
* is only necessary if manipulation of middle_frame occurred.
2012-03-22 19:51:16 +00:00
*
2007-08-08 19:30:52 +00:00
* \param chan Channel that the list is coming off of
* \param audiohook_list List of audiohooks
* \param direction Direction frame is coming in from
* \param frame The frame itself
* \return Return frame on success, NULL on failure
*/
static struct ast_frame * audio_audiohook_write_list ( struct ast_channel * chan , struct ast_audiohook_list * audiohook_list , enum ast_audiohook_direction direction , struct ast_frame * frame )
{
struct ast_frame * start_frame = frame , * middle_frame = frame , * end_frame = frame ;
struct ast_audiohook * audiohook = NULL ;
2011-02-22 23:04:49 +00:00
int samples ;
int middle_frame_manipulated = 0 ;
int removed = 0 ;
2015-05-14 15:21:30 -05:00
int internal_sample_rate ;
2009-10-20 22:09:07 +00:00
2010-04-29 15:33:27 +00:00
/* ---Part_1. translate start_frame to SLINEAR if necessary. */
2011-02-22 23:04:49 +00:00
if ( ! ( middle_frame = audiohook_list_translate_to_slin ( audiohook_list , direction , start_frame ) ) ) {
return frame ;
2007-08-08 19:30:52 +00:00
}
2017-04-26 10:38:31 +00:00
/* If the translation resulted in an interpolated frame then immediately return as audiohooks
* rely on actual media being present to do things.
*/
if ( ! middle_frame - > data . ptr ) {
2017-11-14 18:00:55 -06:00
if ( middle_frame ! = start_frame ) {
ast_frfree ( middle_frame ) ;
}
2017-04-26 10:38:31 +00:00
return start_frame ;
}
2011-02-22 23:04:49 +00:00
samples = middle_frame - > samples ;
2007-08-08 19:30:52 +00:00
2015-05-14 15:21:30 -05:00
/*
* While processing each audiohook check to see if the internal sample rate needs
* to be adjusted (it should be the highest rate specified between formats and
* hooks). The given audiohook_list's internal sample rate is then set to the
* updated value before returning.
*
* If slin compatibility mode is turned on then an audiohook's internal sample
* rate is set to its audiohook_list's rate. If an audiohook_list's rate is
* adjusted during this pass then the change is picked up by the audiohooks
* on the next pass.
*/
internal_sample_rate = audiohook_list - > list_internal_samp_rate ;
2010-04-29 15:33:27 +00:00
/* ---Part_2: Send middle_frame to spy and manipulator lists. middle_frame is guaranteed to be SLINEAR here.*/
2007-08-08 19:30:52 +00:00
/* Queue up signed linear frame to each spy */
AST_LIST_TRAVERSE_SAFE_BEGIN ( & audiohook_list - > spy_list , audiohook , list ) {
ast_audiohook_lock ( audiohook ) ;
if ( audiohook - > status ! = AST_AUDIOHOOK_STATUS_RUNNING ) {
2007-11-08 05:28:47 +00:00
AST_LIST_REMOVE_CURRENT ( list ) ;
2011-02-22 23:04:49 +00:00
removed = 1 ;
2009-11-20 17:26:20 +00:00
ast_audiohook_update_status ( audiohook , AST_AUDIOHOOK_STATUS_DONE ) ;
2007-08-08 19:30:52 +00:00
ast_audiohook_unlock ( audiohook ) ;
2014-10-03 19:42:54 +00:00
if ( ast_channel_is_bridged ( chan ) ) {
ast_channel_set_unbridged_nolock ( chan , 1 ) ;
}
2007-08-08 19:30:52 +00:00
continue ;
}
2015-05-14 15:21:30 -05:00
audiohook_list_set_hook_rate ( audiohook_list , audiohook , & internal_sample_rate ) ;
2007-08-08 19:30:52 +00:00
ast_audiohook_write_frame ( audiohook , direction , middle_frame ) ;
ast_audiohook_unlock ( audiohook ) ;
}
2009-04-10 03:55:27 +00:00
AST_LIST_TRAVERSE_SAFE_END ;
2007-08-08 19:30:52 +00:00
/* If this frame is being written out to the channel then we need to use whisper sources */
2013-11-23 12:40:46 +00:00
if ( ! AST_LIST_EMPTY ( & audiohook_list - > whisper_list ) ) {
2007-08-08 19:30:52 +00:00
int i = 0 ;
short read_buf [ samples ] , combine_buf [ samples ] , * data1 = NULL , * data2 = NULL ;
memset ( & combine_buf , 0 , sizeof ( combine_buf ) ) ;
AST_LIST_TRAVERSE_SAFE_BEGIN ( & audiohook_list - > whisper_list , audiohook , list ) {
2013-11-23 12:40:46 +00:00
struct ast_slinfactory * factory = ( direction = = AST_AUDIOHOOK_DIRECTION_READ ? & audiohook - > read_factory : & audiohook - > write_factory ) ;
2007-08-08 19:30:52 +00:00
ast_audiohook_lock ( audiohook ) ;
if ( audiohook - > status ! = AST_AUDIOHOOK_STATUS_RUNNING ) {
2007-11-08 05:28:47 +00:00
AST_LIST_REMOVE_CURRENT ( list ) ;
2011-02-22 23:04:49 +00:00
removed = 1 ;
2009-11-20 17:26:20 +00:00
ast_audiohook_update_status ( audiohook , AST_AUDIOHOOK_STATUS_DONE ) ;
2007-08-08 19:30:52 +00:00
ast_audiohook_unlock ( audiohook ) ;
2014-10-03 19:42:54 +00:00
if ( ast_channel_is_bridged ( chan ) ) {
ast_channel_set_unbridged_nolock ( chan , 1 ) ;
}
2007-08-08 19:30:52 +00:00
continue ;
}
2015-05-14 15:21:30 -05:00
audiohook_list_set_hook_rate ( audiohook_list , audiohook , & internal_sample_rate ) ;
2013-11-23 12:40:46 +00:00
if ( ast_slinfactory_available ( factory ) > = samples & & ast_slinfactory_read ( factory , read_buf , samples ) ) {
2007-08-08 19:30:52 +00:00
/* Take audio from this whisper source and combine it into our main buffer */
2014-04-11 07:07:36 +00:00
for ( i = 0 , data1 = combine_buf , data2 = read_buf ; i < samples ; i + + , data1 + + , data2 + + ) {
2007-08-08 19:30:52 +00:00
ast_slinear_saturated_add ( data1 , data2 ) ;
2014-04-11 07:07:36 +00:00
}
2007-08-08 19:30:52 +00:00
}
ast_audiohook_unlock ( audiohook ) ;
}
2009-04-10 03:55:27 +00:00
AST_LIST_TRAVERSE_SAFE_END ;
2007-08-08 19:30:52 +00:00
/* We take all of the combined whisper sources and combine them into the audio being written out */
2011-02-22 23:04:49 +00:00
for ( i = 0 , data1 = middle_frame - > data . ptr , data2 = combine_buf ; i < samples ; i + + , data1 + + , data2 + + ) {
2007-08-08 19:30:52 +00:00
ast_slinear_saturated_add ( data1 , data2 ) ;
2011-02-22 23:04:49 +00:00
}
middle_frame_manipulated = 1 ;
2007-08-08 19:30:52 +00:00
}
/* Pass off frame to manipulate audiohooks */
if ( ! AST_LIST_EMPTY ( & audiohook_list - > manipulate_list ) ) {
AST_LIST_TRAVERSE_SAFE_BEGIN ( & audiohook_list - > manipulate_list , audiohook , list ) {
ast_audiohook_lock ( audiohook ) ;
if ( audiohook - > status ! = AST_AUDIOHOOK_STATUS_RUNNING ) {
2007-11-08 05:28:47 +00:00
AST_LIST_REMOVE_CURRENT ( list ) ;
2011-02-22 23:04:49 +00:00
removed = 1 ;
2009-11-20 17:26:20 +00:00
ast_audiohook_update_status ( audiohook , AST_AUDIOHOOK_STATUS_DONE ) ;
2007-08-08 19:30:52 +00:00
ast_audiohook_unlock ( audiohook ) ;
/* We basically drop all of our links to the manipulate audiohook and prod it to do it's own destructive things */
audiohook - > manipulate_callback ( audiohook , chan , NULL , direction ) ;
2014-10-03 19:42:54 +00:00
if ( ast_channel_is_bridged ( chan ) ) {
ast_channel_set_unbridged_nolock ( chan , 1 ) ;
}
2007-08-08 19:30:52 +00:00
continue ;
}
2015-05-14 15:21:30 -05:00
audiohook_list_set_hook_rate ( audiohook_list , audiohook , & internal_sample_rate ) ;
/*
* Feed in frame to manipulation.
*/
2015-07-22 14:24:47 -03:00
if ( ! audiohook - > manipulate_callback ( audiohook , chan , middle_frame , direction ) ) {
/*
* XXX FAILURES ARE IGNORED XXX
* If the manipulation fails then the frame will be returned in its original state.
* Since there are potentially more manipulator callbacks in the list, no action should
* be taken here to exit early.
*/
middle_frame_manipulated = 1 ;
}
2007-08-08 19:30:52 +00:00
ast_audiohook_unlock ( audiohook ) ;
}
2009-04-10 03:55:27 +00:00
AST_LIST_TRAVERSE_SAFE_END ;
2007-08-08 19:30:52 +00:00
}
2010-04-29 15:33:27 +00:00
/* ---Part_3: Decide what to do with the end_frame (whether to transcode or not) */
2011-02-22 23:04:49 +00:00
if ( middle_frame_manipulated ) {
2014-07-20 22:06:33 +00:00
if ( ! ( end_frame = audiohook_list_translate_to_native ( audiohook_list , direction , middle_frame , start_frame - > subclass . format ) ) ) {
2011-02-22 23:04:49 +00:00
/* translation failed, so just pass back the input frame */
end_frame = start_frame ;
2007-08-08 19:30:52 +00:00
}
} else {
2011-02-22 23:04:49 +00:00
end_frame = start_frame ;
}
/* clean up our middle_frame if required */
if ( middle_frame ! = end_frame ) {
2010-04-29 15:33:27 +00:00
ast_frfree ( middle_frame ) ;
2011-02-22 23:04:49 +00:00
middle_frame = NULL ;
}
/* Before returning, if an audiohook got removed, reset samplerate compatibility */
if ( removed ) {
audiohook_list_set_samplerate_compatibility ( audiohook_list ) ;
2015-05-14 15:21:30 -05:00
} else {
/*
* Set the audiohook_list's rate to the updated rate. Note that if a hook
* was removed then the list's internal rate is reset to the default.
*/
audiohook_list - > list_internal_samp_rate = internal_sample_rate ;
2007-08-08 19:30:52 +00:00
}
return end_frame ;
}
2010-07-27 20:59:16 +00:00
int ast_audiohook_write_list_empty ( struct ast_audiohook_list * audiohook_list )
{
2013-07-19 23:30:10 +00:00
return ! audiohook_list
| | ( AST_LIST_EMPTY ( & audiohook_list - > spy_list )
& & AST_LIST_EMPTY ( & audiohook_list - > whisper_list )
& & AST_LIST_EMPTY ( & audiohook_list - > manipulate_list ) ) ;
2010-07-27 20:59:16 +00:00
}
2007-08-08 19:30:52 +00:00
/*! \brief Pass a frame off to be handled by the audiohook core
* \param chan Channel that the list is coming off of
* \param audiohook_list List of audiohooks
* \param direction Direction frame is coming in from
* \param frame The frame itself
* \return Return frame on success, NULL on failure
*/
struct ast_frame * ast_audiohook_write_list ( struct ast_channel * chan , struct ast_audiohook_list * audiohook_list , enum ast_audiohook_direction direction , struct ast_frame * frame )
{
/* Pass off frame to it's respective list write function */
2014-04-11 07:07:36 +00:00
if ( frame - > frametype = = AST_FRAME_VOICE ) {
2007-08-08 19:30:52 +00:00
return audio_audiohook_write_list ( chan , audiohook_list , direction , frame ) ;
2014-04-11 07:07:36 +00:00
} else if ( frame - > frametype = = AST_FRAME_DTMF ) {
2007-08-08 19:30:52 +00:00
return dtmf_audiohook_write_list ( chan , audiohook_list , direction , frame ) ;
2014-04-11 07:07:36 +00:00
} else {
2007-08-08 19:30:52 +00:00
return frame ;
2014-04-11 07:07:36 +00:00
}
2007-08-08 19:30:52 +00:00
}
/*! \brief Wait for audiohook trigger to be triggered
* \param audiohook Audiohook to wait on
*/
void ast_audiohook_trigger_wait ( struct ast_audiohook * audiohook )
{
2008-08-10 19:35:50 +00:00
struct timeval wait ;
2007-08-08 19:30:52 +00:00
struct timespec ts ;
2008-08-10 19:35:50 +00:00
wait = ast_tvadd ( ast_tvnow ( ) , ast_samp2tv ( 50000 , 1000 ) ) ;
ts . tv_sec = wait . tv_sec ;
ts . tv_nsec = wait . tv_usec * 1000 ;
2012-03-22 19:51:16 +00:00
2007-08-08 19:30:52 +00:00
ast_cond_timedwait ( & audiohook - > trigger , & audiohook - > lock , & ts ) ;
2012-03-22 19:51:16 +00:00
2007-08-08 19:30:52 +00:00
return ;
}
2007-11-30 21:19:57 +00:00
/* Count number of channel audiohooks by type, regardless of type */
int ast_channel_audiohook_count_by_source ( struct ast_channel * chan , const char * source , enum ast_audiohook_type type )
{
int count = 0 ;
struct ast_audiohook * ah = NULL ;
2014-04-11 07:07:36 +00:00
if ( ! ast_channel_audiohooks ( chan ) ) {
2007-11-30 21:19:57 +00:00
return - 1 ;
2014-04-11 07:07:36 +00:00
}
2007-11-30 21:19:57 +00:00
switch ( type ) {
case AST_AUDIOHOOK_TYPE_SPY :
2012-02-20 23:43:27 +00:00
AST_LIST_TRAVERSE ( & ast_channel_audiohooks ( chan ) - > spy_list , ah , list ) {
2007-11-30 21:19:57 +00:00
if ( ! strcmp ( ah - > source , source ) ) {
count + + ;
}
}
break ;
case AST_AUDIOHOOK_TYPE_WHISPER :
2012-02-20 23:43:27 +00:00
AST_LIST_TRAVERSE ( & ast_channel_audiohooks ( chan ) - > whisper_list , ah , list ) {
2007-11-30 21:19:57 +00:00
if ( ! strcmp ( ah - > source , source ) ) {
count + + ;
}
}
break ;
case AST_AUDIOHOOK_TYPE_MANIPULATE :
2012-02-20 23:43:27 +00:00
AST_LIST_TRAVERSE ( & ast_channel_audiohooks ( chan ) - > manipulate_list , ah , list ) {
2007-11-30 21:19:57 +00:00
if ( ! strcmp ( ah - > source , source ) ) {
count + + ;
}
}
break ;
default :
2014-05-09 22:49:26 +00:00
ast_debug ( 1 , " Invalid audiohook type supplied, (%u) \n " , type ) ;
2007-11-30 21:19:57 +00:00
return - 1 ;
}
return count ;
}
/* Count number of channel audiohooks by type that are running */
int ast_channel_audiohook_count_by_source_running ( struct ast_channel * chan , const char * source , enum ast_audiohook_type type )
{
int count = 0 ;
struct ast_audiohook * ah = NULL ;
2012-02-20 23:43:27 +00:00
if ( ! ast_channel_audiohooks ( chan ) )
2007-11-30 21:19:57 +00:00
return - 1 ;
switch ( type ) {
case AST_AUDIOHOOK_TYPE_SPY :
2012-02-20 23:43:27 +00:00
AST_LIST_TRAVERSE ( & ast_channel_audiohooks ( chan ) - > spy_list , ah , list ) {
2007-11-30 21:19:57 +00:00
if ( ( ! strcmp ( ah - > source , source ) ) & & ( ah - > status = = AST_AUDIOHOOK_STATUS_RUNNING ) )
count + + ;
}
break ;
case AST_AUDIOHOOK_TYPE_WHISPER :
2012-02-20 23:43:27 +00:00
AST_LIST_TRAVERSE ( & ast_channel_audiohooks ( chan ) - > whisper_list , ah , list ) {
2007-11-30 21:19:57 +00:00
if ( ( ! strcmp ( ah - > source , source ) ) & & ( ah - > status = = AST_AUDIOHOOK_STATUS_RUNNING ) )
count + + ;
}
break ;
case AST_AUDIOHOOK_TYPE_MANIPULATE :
2012-02-20 23:43:27 +00:00
AST_LIST_TRAVERSE ( & ast_channel_audiohooks ( chan ) - > manipulate_list , ah , list ) {
2007-11-30 21:19:57 +00:00
if ( ( ! strcmp ( ah - > source , source ) ) & & ( ah - > status = = AST_AUDIOHOOK_STATUS_RUNNING ) )
count + + ;
}
break ;
default :
2014-05-09 22:49:26 +00:00
ast_debug ( 1 , " Invalid audiohook type supplied, (%u) \n " , type ) ;
2007-11-30 21:19:57 +00:00
return - 1 ;
}
return count ;
}
2008-03-21 17:58:59 +00:00
/*! \brief Audiohook volume adjustment structure */
struct audiohook_volume {
struct ast_audiohook audiohook ; /*!< Audiohook attached to the channel */
int read_adjustment ; /*!< Value to adjust frames read from the channel by */
int write_adjustment ; /*!< Value to adjust frames written to the channel by */
} ;
/*! \brief Callback used to destroy the audiohook volume datastore
* \param data Volume information structure
* \return Returns nothing
*/
static void audiohook_volume_destroy ( void * data )
{
struct audiohook_volume * audiohook_volume = data ;
/* Destroy the audiohook as it is no longer in use */
ast_audiohook_destroy ( & audiohook_volume - > audiohook ) ;
/* Finally free ourselves, we are of no more use */
ast_free ( audiohook_volume ) ;
return ;
}
/*! \brief Datastore used to store audiohook volume information */
static const struct ast_datastore_info audiohook_volume_datastore = {
. type = " Volume " ,
. destroy = audiohook_volume_destroy ,
} ;
/*! \brief Helper function which actually gets called by audiohooks to perform the adjustment
* \param audiohook Audiohook attached to the channel
* \param chan Channel we are attached to
* \param frame Frame of audio we want to manipulate
* \param direction Direction the audio came in from
* \return Returns 0 on success, -1 on failure
*/
static int audiohook_volume_callback ( struct ast_audiohook * audiohook , struct ast_channel * chan , struct ast_frame * frame , enum ast_audiohook_direction direction )
{
struct ast_datastore * datastore = NULL ;
struct audiohook_volume * audiohook_volume = NULL ;
int * gain = NULL ;
/* If the audiohook is shutting down don't even bother */
if ( audiohook - > status = = AST_AUDIOHOOK_STATUS_DONE ) {
return 0 ;
}
/* Try to find the datastore containg adjustment information, if we can't just bail out */
if ( ! ( datastore = ast_channel_datastore_find ( chan , & audiohook_volume_datastore , NULL ) ) ) {
return 0 ;
}
audiohook_volume = datastore - > data ;
/* Based on direction grab the appropriate adjustment value */
if ( direction = = AST_AUDIOHOOK_DIRECTION_READ ) {
gain = & audiohook_volume - > read_adjustment ;
} else if ( direction = = AST_AUDIOHOOK_DIRECTION_WRITE ) {
gain = & audiohook_volume - > write_adjustment ;
}
/* If an adjustment value is present modify the frame */
if ( gain & & * gain ) {
ast_frame_adjust_volume ( frame , * gain ) ;
}
return 0 ;
}
/*! \brief Helper function which finds and optionally creates an audiohook_volume_datastore datastore on a channel
* \param chan Channel to look on
* \param create Whether to create the datastore if not found
* \return Returns audiohook_volume structure on success, NULL on failure
*/
static struct audiohook_volume * audiohook_volume_get ( struct ast_channel * chan , int create )
{
struct ast_datastore * datastore = NULL ;
struct audiohook_volume * audiohook_volume = NULL ;
/* If we are able to find the datastore return the contents (which is actually an audiohook_volume structure) */
if ( ( datastore = ast_channel_datastore_find ( chan , & audiohook_volume_datastore , NULL ) ) ) {
return datastore - > data ;
}
/* If we are not allowed to create a datastore or if we fail to create a datastore, bail out now as we have nothing for them */
2008-08-05 16:56:11 +00:00
if ( ! create | | ! ( datastore = ast_datastore_alloc ( & audiohook_volume_datastore , NULL ) ) ) {
2008-03-21 17:58:59 +00:00
return NULL ;
}
/* Create a new audiohook_volume structure to contain our adjustments and audiohook */
if ( ! ( audiohook_volume = ast_calloc ( 1 , sizeof ( * audiohook_volume ) ) ) ) {
2008-08-05 16:56:11 +00:00
ast_datastore_free ( datastore ) ;
2008-03-21 17:58:59 +00:00
return NULL ;
}
/* Setup our audiohook structure so we can manipulate the audio */
2011-02-22 23:04:49 +00:00
ast_audiohook_init ( & audiohook_volume - > audiohook , AST_AUDIOHOOK_TYPE_MANIPULATE , " Volume " , AST_AUDIOHOOK_MANIPULATE_ALL_RATES ) ;
2008-03-21 17:58:59 +00:00
audiohook_volume - > audiohook . manipulate_callback = audiohook_volume_callback ;
/* Attach the audiohook_volume blob to the datastore and attach to the channel */
datastore - > data = audiohook_volume ;
ast_channel_datastore_add ( chan , datastore ) ;
/* All is well... put the audiohook into motion */
ast_audiohook_attach ( chan , & audiohook_volume - > audiohook ) ;
return audiohook_volume ;
}
/*! \brief Adjust the volume on frames read from or written to a channel
* \param chan Channel to muck with
* \param direction Direction to set on
* \param volume Value to adjust the volume by
* \return Returns 0 on success, -1 on failure
*/
int ast_audiohook_volume_set ( struct ast_channel * chan , enum ast_audiohook_direction direction , int volume )
{
struct audiohook_volume * audiohook_volume = NULL ;
/* Attempt to find the audiohook volume information, but only create it if we are not setting the adjustment value to zero */
if ( ! ( audiohook_volume = audiohook_volume_get ( chan , ( volume ? 1 : 0 ) ) ) ) {
return - 1 ;
}
/* Now based on the direction set the proper value */
if ( direction = = AST_AUDIOHOOK_DIRECTION_READ | | direction = = AST_AUDIOHOOK_DIRECTION_BOTH ) {
audiohook_volume - > read_adjustment = volume ;
2009-03-02 14:13:45 +00:00
}
if ( direction = = AST_AUDIOHOOK_DIRECTION_WRITE | | direction = = AST_AUDIOHOOK_DIRECTION_BOTH ) {
2008-03-21 17:58:59 +00:00
audiohook_volume - > write_adjustment = volume ;
}
return 0 ;
}
/*! \brief Retrieve the volume adjustment value on frames read from or written to a channel
* \param chan Channel to retrieve volume adjustment from
* \param direction Direction to retrieve
* \return Returns adjustment value
*/
int ast_audiohook_volume_get ( struct ast_channel * chan , enum ast_audiohook_direction direction )
{
struct audiohook_volume * audiohook_volume = NULL ;
int adjustment = 0 ;
/* Attempt to find the audiohook volume information, but do not create it as we only want to look at the values */
if ( ! ( audiohook_volume = audiohook_volume_get ( chan , 0 ) ) ) {
return 0 ;
}
/* Grab the adjustment value based on direction given */
if ( direction = = AST_AUDIOHOOK_DIRECTION_READ ) {
adjustment = audiohook_volume - > read_adjustment ;
} else if ( direction = = AST_AUDIOHOOK_DIRECTION_WRITE ) {
adjustment = audiohook_volume - > write_adjustment ;
}
return adjustment ;
}
/*! \brief Adjust the volume on frames read from or written to a channel
* \param chan Channel to muck with
* \param direction Direction to increase
* \param volume Value to adjust the adjustment by
* \return Returns 0 on success, -1 on failure
*/
int ast_audiohook_volume_adjust ( struct ast_channel * chan , enum ast_audiohook_direction direction , int volume )
{
struct audiohook_volume * audiohook_volume = NULL ;
/* Attempt to find the audiohook volume information, and create an audiohook if none exists */
if ( ! ( audiohook_volume = audiohook_volume_get ( chan , 1 ) ) ) {
return - 1 ;
}
/* Based on the direction change the specific adjustment value */
if ( direction = = AST_AUDIOHOOK_DIRECTION_READ | | direction = = AST_AUDIOHOOK_DIRECTION_BOTH ) {
audiohook_volume - > read_adjustment + = volume ;
2009-03-02 14:13:45 +00:00
}
if ( direction = = AST_AUDIOHOOK_DIRECTION_WRITE | | direction = = AST_AUDIOHOOK_DIRECTION_BOTH ) {
2008-03-21 17:58:59 +00:00
audiohook_volume - > write_adjustment + = volume ;
}
return 0 ;
}
2010-04-21 11:27:27 +00:00
/*! \brief Mute frames read from or written to a channel
* \param chan Channel to muck with
* \param source Type of audiohook
* \param flag which flag to set / clear
* \param clear set or clear
* \return Returns 0 on success, -1 on failure
*/
int ast_audiohook_set_mute ( struct ast_channel * chan , const char * source , enum ast_audiohook_flags flag , int clear )
{
struct ast_audiohook * audiohook = NULL ;
ast_channel_lock ( chan ) ;
/* Ensure the channel has audiohooks on it */
2012-02-20 23:43:27 +00:00
if ( ! ast_channel_audiohooks ( chan ) ) {
2010-04-21 11:27:27 +00:00
ast_channel_unlock ( chan ) ;
return - 1 ;
}
2012-02-20 23:43:27 +00:00
audiohook = find_audiohook_by_source ( ast_channel_audiohooks ( chan ) , source ) ;
2010-04-21 11:27:27 +00:00
if ( audiohook ) {
if ( clear ) {
ast_clear_flag ( audiohook , flag ) ;
} else {
ast_set_flag ( audiohook , flag ) ;
}
}
ast_channel_unlock ( chan ) ;
return ( audiohook ? 0 : - 1 ) ;
}