Replace current spy architecture with backport of audiohooks. This should take care of current known spy issues.

git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/1.4@98972 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Joshua Colp
2008-01-16 20:33:47 +00:00
parent 1156daa2e4
commit fa640604de
10 changed files with 1151 additions and 912 deletions

View File

@@ -40,7 +40,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/chanspy.h"
#include "asterisk/audiohook.h"
#include "asterisk/features.h"
#include "asterisk/options.h"
#include "asterisk/app.h"
@@ -143,7 +143,8 @@ AST_APP_OPTIONS(spy_opts, {
struct chanspy_translation_helper {
/* spy data */
struct ast_channel_spy spy;
struct ast_audiohook spy_audiohook;
struct ast_audiohook whisper_audiohook;
int fd;
int volfactor;
};
@@ -163,15 +164,17 @@ static int spy_generate(struct ast_channel *chan, void *data, int len, int sampl
{
struct chanspy_translation_helper *csth = data;
struct ast_frame *f;
if (csth->spy.status != CHANSPY_RUNNING)
/* Channel is already gone more than likely */
return -1;
ast_mutex_lock(&csth->spy.lock);
f = ast_channel_spy_read_frame(&csth->spy, samples);
ast_mutex_unlock(&csth->spy.lock);
ast_audiohook_lock(&csth->spy_audiohook);
if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
ast_audiohook_unlock(&csth->spy_audiohook);
return -1;
}
f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR);
ast_audiohook_unlock(&csth->spy_audiohook);
if (!f)
return 0;
@@ -194,16 +197,14 @@ static struct ast_generator spygen = {
.generate = spy_generate,
};
static int start_spying(struct ast_channel *chan, struct ast_channel *spychan, struct ast_channel_spy *spy)
static int start_spying(struct ast_channel *chan, struct ast_channel *spychan, struct ast_audiohook *audiohook)
{
int res;
struct ast_channel *peer;
ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan->name, chan->name);
ast_channel_lock(chan);
res = ast_channel_spy_add(chan, spy);
ast_channel_unlock(chan);
res = ast_audiohook_attach(chan, audiohook);
if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan)))
ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
@@ -211,35 +212,6 @@ static int start_spying(struct ast_channel *chan, struct ast_channel *spychan, s
return res;
}
/* Map 'volume' levels from -4 through +4 into
decibel (dB) settings for channel drivers
*/
static signed char volfactor_map[] = {
-24,
-18,
-12,
-6,
0,
6,
12,
18,
24,
};
/* attempt to set the desired gain adjustment via the channel driver;
if successful, clear it out of the csth structure so the
generator will not attempt to do the adjustment itself
*/
static void set_volume(struct ast_channel *chan, struct chanspy_translation_helper *csth)
{
signed char volume_adjust = volfactor_map[csth->volfactor + 4];
if (!ast_channel_setoption(chan, AST_OPTION_TXGAIN, &volume_adjust, sizeof(volume_adjust), 0))
csth->volfactor = 0;
csth->spy.read_vol_adjustment = csth->volfactor;
csth->spy.write_vol_adjustment = csth->volfactor;
}
static int channel_spy(struct ast_channel *chan, struct ast_channel *spyee, int *volfactor, int fd,
const struct ast_flags *flags)
{
@@ -258,49 +230,27 @@ static int channel_spy(struct ast_channel *chan, struct ast_channel *spyee, int
ast_verbose(VERBOSE_PREFIX_2 "Spying on channel %s\n", name);
memset(&csth, 0, sizeof(csth));
ast_set_flag(&csth.spy, CHANSPY_FORMAT_AUDIO);
ast_set_flag(&csth.spy, CHANSPY_TRIGGER_NONE);
ast_set_flag(&csth.spy, CHANSPY_MIXAUDIO);
csth.spy.type = "ChanSpy";
csth.spy.status = CHANSPY_RUNNING;
csth.spy.read_queue.format = AST_FORMAT_SLINEAR;
csth.spy.write_queue.format = AST_FORMAT_SLINEAR;
ast_mutex_init(&csth.spy.lock);
csth.volfactor = *volfactor;
set_volume(chan, &csth);
if (csth.volfactor) {
ast_set_flag(&csth.spy, CHANSPY_READ_VOLADJUST);
csth.spy.read_vol_adjustment = csth.volfactor;
ast_set_flag(&csth.spy, CHANSPY_WRITE_VOLADJUST);
csth.spy.write_vol_adjustment = csth.volfactor;
}
csth.fd = fd;
if (start_spying(spyee, chan, &csth.spy)) {
ast_mutex_destroy(&csth.spy.lock);
ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy");
if (start_spying(spyee, chan, &csth.spy_audiohook)) {
ast_audiohook_destroy(&csth.spy_audiohook);
return 0;
}
if (ast_test_flag(flags, OPTION_WHISPER)) {
struct ast_filestream *beepstream;
int old_write_format = 0;
ast_channel_whisper_start(csth.spy.chan);
old_write_format = chan->writeformat;
if ((beepstream = ast_openstream_full(chan, "beep", chan->language, 1))) {
struct ast_frame *f;
while ((f = ast_readframe(beepstream))) {
ast_channel_whisper_feed(csth.spy.chan, f);
ast_frfree(f);
}
ast_closestream(beepstream);
chan->stream = NULL;
}
if (old_write_format)
ast_set_write_format(chan, old_write_format);
ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy");
start_spying(spyee, chan, &csth.whisper_audiohook);
}
csth.volfactor = *volfactor;
if (csth.volfactor) {
csth.spy_audiohook.options.read_volume = csth.volfactor;
csth.spy_audiohook.options.write_volume = csth.volfactor;
}
csth.fd = fd;
if (ast_test_flag(flags, OPTION_PRIVATE))
silgen = ast_channel_start_silence_generator(chan);
@@ -321,17 +271,16 @@ static int channel_spy(struct ast_channel *chan, struct ast_channel *spyee, int
has arrived, since the spied-on channel could have gone away while
we were waiting
*/
while ((res = ast_waitfor(chan, -1) > -1) &&
csth.spy.status == CHANSPY_RUNNING &&
csth.spy.chan) {
while ((res = ast_waitfor(chan, -1) > -1) && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
running = -1;
break;
}
if (ast_test_flag(flags, OPTION_WHISPER) &&
(f->frametype == AST_FRAME_VOICE)) {
ast_channel_whisper_feed(csth.spy.chan, f);
if (ast_test_flag(flags, OPTION_WHISPER) && (f->frametype == AST_FRAME_VOICE)) {
ast_audiohook_lock(&csth.whisper_audiohook);
ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
ast_audiohook_unlock(&csth.whisper_audiohook);
ast_frfree(f);
continue;
}
@@ -364,38 +313,29 @@ static int channel_spy(struct ast_channel *chan, struct ast_channel *spyee, int
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "Setting spy volume on %s to %d\n", chan->name, *volfactor);
csth.volfactor = *volfactor;
set_volume(chan, &csth);
if (csth.volfactor) {
ast_set_flag(&csth.spy, CHANSPY_READ_VOLADJUST);
csth.spy.read_vol_adjustment = csth.volfactor;
ast_set_flag(&csth.spy, CHANSPY_WRITE_VOLADJUST);
csth.spy.write_vol_adjustment = csth.volfactor;
} else {
ast_clear_flag(&csth.spy, CHANSPY_READ_VOLADJUST);
ast_clear_flag(&csth.spy, CHANSPY_WRITE_VOLADJUST);
}
csth.spy_audiohook.options.read_volume = csth.volfactor;
csth.spy_audiohook.options.write_volume = csth.volfactor;
} else if (res >= '0' && res <= '9') {
inp[x++] = res;
}
}
if (ast_test_flag(flags, OPTION_WHISPER) && csth.spy.chan)
ast_channel_whisper_stop(csth.spy.chan);
if (ast_test_flag(flags, OPTION_PRIVATE))
ast_channel_stop_silence_generator(chan, silgen);
else
ast_deactivate_generator(chan);
csth.spy.status = CHANSPY_DONE;
/* If a channel still exists on our spy structure then we need to remove ourselves */
if (csth.spy.chan) {
ast_channel_lock(csth.spy.chan);
ast_channel_spy_remove(csth.spy.chan, &csth.spy);
ast_channel_unlock(csth.spy.chan);
if (ast_test_flag(flags, OPTION_WHISPER)) {
ast_audiohook_lock(&csth.whisper_audiohook);
ast_audiohook_detach(&csth.whisper_audiohook);
ast_audiohook_unlock(&csth.whisper_audiohook);
ast_audiohook_destroy(&csth.whisper_audiohook);
}
ast_channel_spy_free(&csth.spy);
ast_audiohook_lock(&csth.spy_audiohook);
ast_audiohook_detach(&csth.spy_audiohook);
ast_audiohook_unlock(&csth.spy_audiohook);
ast_audiohook_destroy(&csth.spy_audiohook);
if (option_verbose >= 2)
ast_verbose(VERBOSE_PREFIX_2 "Done Spying on channel %s\n", name);

View File

@@ -1578,7 +1578,7 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int c
goto outrun;
}
retryzap = (strcasecmp(chan->tech->type, "Zap") || (chan->spies || chan->monitor) ? 1 : 0);
retryzap = (strcasecmp(chan->tech->type, "Zap") || (chan->audiohooks || chan->monitor) ? 1 : 0);
user->zapchannel = !retryzap;
zapretry:
@@ -1896,14 +1896,14 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int c
break;
if (c) {
if (c->fds[0] != origfd || (user->zapchannel && (c->spies || c->monitor))) {
if (c->fds[0] != origfd || (user->zapchannel && (c->audiohooks || c->monitor))) {
if (using_pseudo) {
/* Kill old pseudo */
close(fd);
using_pseudo = 0;
}
ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
retryzap = (strcasecmp(c->tech->type, "Zap") || (c->spies || c->monitor) ? 1 : 0);
retryzap = (strcasecmp(c->tech->type, "Zap") || (c->audiohooks || c->monitor) ? 1 : 0);
user->zapchannel = !retryzap;
goto zapretry;
}

View File

@@ -45,7 +45,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/chanspy.h"
#include "asterisk/audiohook.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/lock.h"
@@ -93,11 +93,12 @@ struct module_symbols *me;
static const char *mixmonitor_spy_type = "MixMonitor";
struct mixmonitor {
struct ast_channel_spy spy;
struct ast_audiohook audiohook;
char *filename;
char *post_process;
char *name;
unsigned int flags;
struct ast_channel *chan;
};
enum {
@@ -123,7 +124,7 @@ AST_APP_OPTIONS(mixmonitor_opts, {
AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME),
});
static int startmon(struct ast_channel *chan, struct ast_channel_spy *spy)
static int startmon(struct ast_channel *chan, struct ast_audiohook *audiohook)
{
struct ast_channel *peer;
int res;
@@ -131,9 +132,7 @@ static int startmon(struct ast_channel *chan, struct ast_channel_spy *spy)
if (!chan)
return -1;
ast_channel_lock(chan);
res = ast_channel_spy_add(chan, spy);
ast_channel_unlock(chan);
res = ast_audiohook_attach(chan, audiohook);
if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan)))
ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
@@ -146,7 +145,6 @@ static int startmon(struct ast_channel *chan, struct ast_channel_spy *spy)
static void *mixmonitor_thread(void *obj)
{
struct mixmonitor *mixmonitor = obj;
struct ast_frame *f = NULL;
struct ast_filestream *fs = NULL;
unsigned int oflags;
char *ext;
@@ -155,58 +153,48 @@ static void *mixmonitor_thread(void *obj)
if (option_verbose > 1)
ast_verbose(VERBOSE_PREFIX_2 "Begin MixMonitor Recording %s\n", mixmonitor->name);
ast_mutex_lock(&mixmonitor->spy.lock);
ast_audiohook_lock(&mixmonitor->audiohook);
while (mixmonitor->spy.chan) {
struct ast_frame *next;
int write;
ast_channel_spy_trigger_wait(&mixmonitor->spy);
while (mixmonitor->audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
struct ast_frame *fr = NULL;
if (!mixmonitor->spy.chan || mixmonitor->spy.status != CHANSPY_RUNNING)
ast_audiohook_trigger_wait(&mixmonitor->audiohook);
if (mixmonitor->audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING)
break;
while (1) {
if (!(f = ast_channel_spy_read_frame(&mixmonitor->spy, SAMPLES_PER_FRAME)))
break;
write = (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) ||
ast_bridged_channel(mixmonitor->spy.chan));
/* it is possible for ast_channel_spy_read_frame() to return a chain
of frames if a queue flush was necessary, so process them
*/
for (; f; f = next) {
next = AST_LIST_NEXT(f, frame_list);
if (write && errflag == 0) {
if (!fs) {
/* Determine creation flags and filename plus extension for filestream */
oflags = O_CREAT | O_WRONLY;
oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
if ((ext = strrchr(mixmonitor->filename, '.')))
*(ext++) = '\0';
else
ext = "raw";
/* Move onto actually creating the filestream */
if (!(fs = ast_writefile(mixmonitor->filename, ext, NULL, oflags, 0, 0644))) {
ast_log(LOG_ERROR, "Cannot open %s.%s\n", mixmonitor->filename, ext);
errflag = 1;
}
}
if (fs)
ast_writestream(fs, f);
if (!(fr = ast_audiohook_read_frame(&mixmonitor->audiohook, SAMPLES_PER_FRAME, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR)))
continue;
if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || ast_bridged_channel(mixmonitor->chan)) {
/* Initialize the file if not already done so */
if (!fs && !errflag) {
oflags = O_CREAT | O_WRONLY;
oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
if ((ext = strrchr(mixmonitor->filename, '.')))
*(ext++) = '\0';
else
ext = "raw";
if (!(fs = ast_writefile(mixmonitor->filename, ext, NULL, oflags, 0, 0644))) {
ast_log(LOG_ERROR, "Cannot open %s.%s\n", mixmonitor->filename, ext);
errflag = 1;
}
ast_frame_free(f, 0);
}
/* Write out the frame */
if (fs)
ast_writestream(fs, fr);
}
/* All done! free it. */
ast_frame_free(fr, 0);
}
ast_mutex_unlock(&mixmonitor->spy.lock);
ast_channel_spy_free(&mixmonitor->spy);
ast_audiohook_detach(&mixmonitor->audiohook);
ast_audiohook_unlock(&mixmonitor->audiohook);
ast_audiohook_destroy(&mixmonitor->audiohook);
if (option_verbose > 1)
ast_verbose(VERBOSE_PREFIX_2 "End MixMonitor Recording %s\n", mixmonitor->name);
@@ -271,27 +259,23 @@ static void launch_monitor_thread(struct ast_channel *chan, const char *filename
strcpy(mixmonitor->filename, filename);
/* Setup the actual spy before creating our thread */
ast_set_flag(&mixmonitor->spy, CHANSPY_FORMAT_AUDIO);
ast_set_flag(&mixmonitor->spy, CHANSPY_MIXAUDIO);
mixmonitor->spy.type = mixmonitor_spy_type;
mixmonitor->spy.status = CHANSPY_RUNNING;
mixmonitor->spy.read_queue.format = AST_FORMAT_SLINEAR;
mixmonitor->spy.write_queue.format = AST_FORMAT_SLINEAR;
if (readvol) {
ast_set_flag(&mixmonitor->spy, CHANSPY_READ_VOLADJUST);
mixmonitor->spy.read_vol_adjustment = readvol;
if (ast_audiohook_init(&mixmonitor->audiohook, AST_AUDIOHOOK_TYPE_SPY, mixmonitor_spy_type)) {
free(mixmonitor);
return;
}
if (writevol) {
ast_set_flag(&mixmonitor->spy, CHANSPY_WRITE_VOLADJUST);
mixmonitor->spy.write_vol_adjustment = writevol;
}
ast_mutex_init(&mixmonitor->spy.lock);
ast_set_flag(&mixmonitor->audiohook, AST_AUDIOHOOK_TRIGGER_WRITE);
if (readvol)
mixmonitor->audiohook.options.read_volume = readvol;
if (writevol)
mixmonitor->audiohook.options.write_volume = writevol;
if (startmon(chan, &mixmonitor->spy)) {
if (startmon(chan, &mixmonitor->audiohook)) {
ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
mixmonitor->spy.type, chan->name);
mixmonitor_spy_type, chan->name);
/* Since we couldn't add ourselves - bail out! */
ast_mutex_destroy(&mixmonitor->spy.lock);
ast_audiohook_destroy(&mixmonitor->audiohook);
free(mixmonitor);
return;
}
@@ -391,9 +375,7 @@ static int stop_mixmonitor_exec(struct ast_channel *chan, void *data)
u = ast_module_user_add(chan);
ast_channel_lock(chan);
ast_channel_spy_stop_by_type(chan, mixmonitor_spy_type);
ast_channel_unlock(chan);
ast_audiohook_detach_source(chan, mixmonitor_spy_type);
ast_module_user_remove(u);
@@ -415,7 +397,7 @@ static int mixmonitor_cli(int fd, int argc, char **argv)
if (!strcasecmp(argv[1], "start"))
mixmonitor_exec(chan, argv[3]);
else if (!strcasecmp(argv[1], "stop"))
ast_channel_spy_stop_by_type(chan, mixmonitor_spy_type);
ast_audiohook_detach_source(chan, mixmonitor_spy_type);
ast_channel_unlock(chan);

View File

@@ -0,0 +1,358 @@
/*
* 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
*/
#ifndef _ASTERISK_AUDIOHOOK_H
#define _ASTERISK_AUDIOHOOK_H
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
#include "asterisk/slinfactory.h"
enum ast_audiohook_type {
AST_AUDIOHOOK_TYPE_SPY = 0, /*!< Audiohook wants to receive audio */
AST_AUDIOHOOK_TYPE_WHISPER, /*!< Audiohook wants to provide audio to be mixed with existing audio */
AST_AUDIOHOOK_TYPE_MANIPULATE, /*!< Audiohook wants to manipulate the audio */
};
enum ast_audiohook_status {
AST_AUDIOHOOK_STATUS_NEW = 0, /*!< Audiohook was just created, not in use yet */
AST_AUDIOHOOK_STATUS_RUNNING, /*!< Audiohook is running on a channel */
AST_AUDIOHOOK_STATUS_SHUTDOWN, /*!< Audiohook is being shutdown */
AST_AUDIOHOOK_STATUS_DONE, /*!< Audiohook has shutdown and is not running on a channel any longer */
};
enum ast_audiohook_direction {
AST_AUDIOHOOK_DIRECTION_READ = 0, /*!< Reading audio in */
AST_AUDIOHOOK_DIRECTION_WRITE, /*!< Writing audio out */
AST_AUDIOHOOK_DIRECTION_BOTH, /*!< Both reading audio in and writing audio out */
};
enum ast_audiohook_flags {
AST_AUDIOHOOK_TRIGGER_MODE = (3 << 0), /*!< When audiohook should be triggered to do something */
AST_AUDIOHOOK_TRIGGER_READ = (1 << 0), /*!< Audiohook wants to be triggered when reading audio in */
AST_AUDIOHOOK_TRIGGER_WRITE = (2 << 0), /*!< Audiohook wants to be triggered when writing audio out */
AST_AUDIOHOOK_WANTS_DTMF = (1 << 1), /*!< Audiohook also wants to receive DTMF frames */
};
struct ast_audiohook;
/*! \brief Callback function for manipulate audiohook type
* \param audiohook Audiohook structure
* \param chan Channel
* \param frame Frame of audio to manipulate
* \param direction Direction frame came from
* \return Returns 0 on success, -1 on failure
* \note An audiohook does not have any reference to a private data structure for manipulate types. It is up to the manipulate callback to store this data
* via it's own method. An example would be datastores.
*/
typedef int (*ast_audiohook_manipulate_callback)(struct ast_audiohook *audiohook, struct ast_channel *chan, struct ast_frame *frame, enum ast_audiohook_direction direction);
struct ast_audiohook_options {
int read_volume; /*!< Volume adjustment on frames read from the channel the hook is on */
int write_volume; /*!< Volume adjustment on frames written to the channel the hook is on */
};
struct ast_audiohook {
ast_mutex_t lock; /*!< Lock that protects the audiohook structure */
ast_cond_t trigger; /*!< Trigger condition (if enabled) */
enum ast_audiohook_type type; /*!< Type of audiohook */
enum ast_audiohook_status status; /*!< Status of the audiohook */
const char *source; /*!< Who this audiohook ultimately belongs to */
unsigned int flags; /*!< Flags on the audiohook */
struct ast_slinfactory read_factory; /*!< Factory where frames read from the channel, or read from the whisper source will go through */
struct ast_slinfactory write_factory; /*!< Factory where frames written to the channel will go through */
int format; /*!< Format translation path is setup as */
struct ast_trans_pvt *trans_pvt; /*!< Translation path for reading frames */
ast_audiohook_manipulate_callback manipulate_callback; /*!< Manipulation callback */
struct ast_audiohook_options options; /*!< Applicable options */
AST_LIST_ENTRY(ast_audiohook) list; /*!< Linked list information */
};
struct ast_audiohook_list;
/*! \brief Initialize an audiohook structure
* \param audiohook Audiohook structure
* \param type Type of audiohook to initialize this as
* \param source Who is initializing this audiohook
* \return Returns 0 on success, -1 on failure
*/
int ast_audiohook_init(struct ast_audiohook *audiohook, enum ast_audiohook_type type, const char *source);
/*! \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);
/*! \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);
/*! \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
* \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, int format);
/*! \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);
/*! \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);
/*! \brief Detach audiohooks from list and destroy said list
* \param audiohook_list List of audiohooks
* \return Returns 0 on success, -1 on failure
*/
int ast_audiohook_detach_list(struct ast_audiohook_list *audiohook_list);
/*! \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);
/*! \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);
/*! \brief Wait for audiohook trigger to be triggered
* \param audiohook Audiohook to wait on
*/
void ast_audiohook_trigger_wait(struct ast_audiohook *audiohook);
/*! \brief Lock an audiohook
* \param ah Audiohook structure
*/
#define ast_audiohook_lock(ah) ast_mutex_lock(&(ah)->lock)
/*! \brief Unlock an audiohook
* \param ah Audiohook structure
*/
#define ast_audiohook_unlock(ah) ast_mutex_unlock(&(ah)->lock)
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
#endif /* _ASTERISK_AUDIOHOOK_H */
/*
* 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
*/
#ifndef _ASTERISK_AUDIOHOOK_H
#define _ASTERISK_AUDIOHOOK_H
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
#include "asterisk/slinfactory.h"
enum ast_audiohook_type {
AST_AUDIOHOOK_TYPE_SPY = 0, /*!< Audiohook wants to receive audio */
AST_AUDIOHOOK_TYPE_WHISPER, /*!< Audiohook wants to provide audio to be mixed with existing audio */
AST_AUDIOHOOK_TYPE_MANIPULATE, /*!< Audiohook wants to manipulate the audio */
};
enum ast_audiohook_status {
AST_AUDIOHOOK_STATUS_NEW = 0, /*!< Audiohook was just created, not in use yet */
AST_AUDIOHOOK_STATUS_RUNNING, /*!< Audiohook is running on a channel */
AST_AUDIOHOOK_STATUS_SHUTDOWN, /*!< Audiohook is being shutdown */
AST_AUDIOHOOK_STATUS_DONE, /*!< Audiohook has shutdown and is not running on a channel any longer */
};
enum ast_audiohook_direction {
AST_AUDIOHOOK_DIRECTION_READ = 0, /*!< Reading audio in */
AST_AUDIOHOOK_DIRECTION_WRITE, /*!< Writing audio out */
AST_AUDIOHOOK_DIRECTION_BOTH, /*!< Both reading audio in and writing audio out */
};
enum ast_audiohook_flags {
AST_AUDIOHOOK_TRIGGER_MODE = (3 << 0), /*!< When audiohook should be triggered to do something */
AST_AUDIOHOOK_TRIGGER_READ = (1 << 0), /*!< Audiohook wants to be triggered when reading audio in */
AST_AUDIOHOOK_TRIGGER_WRITE = (2 << 0), /*!< Audiohook wants to be triggered when writing audio out */
AST_AUDIOHOOK_WANTS_DTMF = (1 << 1), /*!< Audiohook also wants to receive DTMF frames */
};
struct ast_audiohook;
/*! \brief Callback function for manipulate audiohook type
* \param audiohook Audiohook structure
* \param chan Channel
* \param frame Frame of audio to manipulate
* \param direction Direction frame came from
* \return Returns 0 on success, -1 on failure
* \note An audiohook does not have any reference to a private data structure for manipulate types. It is up to the manipulate callback to store this data
* via it's own method. An example would be datastores.
*/
typedef int (*ast_audiohook_manipulate_callback)(struct ast_audiohook *audiohook, struct ast_channel *chan, struct ast_frame *frame, enum ast_audiohook_direction direction);
struct ast_audiohook_options {
int read_volume; /*!< Volume adjustment on frames read from the channel the hook is on */
int write_volume; /*!< Volume adjustment on frames written to the channel the hook is on */
};
struct ast_audiohook {
ast_mutex_t lock; /*!< Lock that protects the audiohook structure */
ast_cond_t trigger; /*!< Trigger condition (if enabled) */
enum ast_audiohook_type type; /*!< Type of audiohook */
enum ast_audiohook_status status; /*!< Status of the audiohook */
const char *source; /*!< Who this audiohook ultimately belongs to */
unsigned int flags; /*!< Flags on the audiohook */
struct ast_slinfactory read_factory; /*!< Factory where frames read from the channel, or read from the whisper source will go through */
struct ast_slinfactory write_factory; /*!< Factory where frames written to the channel will go through */
int format; /*!< Format translation path is setup as */
struct ast_trans_pvt *trans_pvt; /*!< Translation path for reading frames */
ast_audiohook_manipulate_callback manipulate_callback; /*!< Manipulation callback */
struct ast_audiohook_options options; /*!< Applicable options */
AST_LIST_ENTRY(ast_audiohook) list; /*!< Linked list information */
};
struct ast_audiohook_list;
/*! \brief Initialize an audiohook structure
* \param audiohook Audiohook structure
* \param type Type of audiohook to initialize this as
* \param source Who is initializing this audiohook
* \return Returns 0 on success, -1 on failure
*/
int ast_audiohook_init(struct ast_audiohook *audiohook, enum ast_audiohook_type type, const char *source);
/*! \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);
/*! \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);
/*! \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
* \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, int format);
/*! \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);
/*! \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);
/*! \brief Detach audiohooks from list and destroy said list
* \param audiohook_list List of audiohooks
* \return Returns 0 on success, -1 on failure
*/
int ast_audiohook_detach_list(struct ast_audiohook_list *audiohook_list);
/*! \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);
/*! \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);
/*! \brief Wait for audiohook trigger to be triggered
* \param audiohook Audiohook to wait on
*/
void ast_audiohook_trigger_wait(struct ast_audiohook *audiohook);
/*! \brief Lock an audiohook
* \param ah Audiohook structure
*/
#define ast_audiohook_lock(ah) ast_mutex_lock(&(ah)->lock)
/*! \brief Unlock an audiohook
* \param ah Audiohook structure
*/
#define ast_audiohook_unlock(ah) ast_mutex_unlock(&(ah)->lock)
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
#endif /* _ASTERISK_AUDIOHOOK_H */

View File

@@ -276,9 +276,6 @@ struct ast_channel_tech {
int (* set_base_channel)(struct ast_channel *chan, struct ast_channel *base);
};
struct ast_channel_spy_list;
struct ast_channel_whisper_buffer;
#define DEBUGCHAN_FLAG 0x80000000
#define FRAMECOUNT_INC(x) ( ((x) & DEBUGCHAN_FLAG) | (((x)+1) & ~DEBUGCHAN_FLAG) )
@@ -430,8 +427,8 @@ struct ast_channel {
int rawreadformat; /*!< Raw read format */
int rawwriteformat; /*!< Raw write format */
struct ast_channel_spy_list *spies; /*!< Chan Spy stuff */
struct ast_channel_whisper_buffer *whisper; /*!< Whisper Paging buffer */
struct ast_audiohook_list *audiohooks;
AST_LIST_ENTRY(ast_channel) chan_list; /*!< For easy linking */
struct ast_jb jb; /*!< The jitterbuffer state */

View File

@@ -1,150 +0,0 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2006, Digium, Inc.
*
* Mark Spencer <markster@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 Asterisk PBX channel spy definitions
*/
#ifndef _ASTERISK_CHANSPY_H
#define _ASTERISK_CHANSPY_H
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
#include "asterisk/linkedlists.h"
enum chanspy_states {
CHANSPY_NEW = 0, /*!< spy not yet operating */
CHANSPY_RUNNING = 1, /*!< normal operation, spy is still operating */
CHANSPY_DONE = 2, /*!< spy is stopped and already removed from channel */
CHANSPY_STOP = 3, /*!< spy requested to stop, still attached to channel */
};
enum chanspy_flags {
CHANSPY_MIXAUDIO = (1 << 0),
CHANSPY_READ_VOLADJUST = (1 << 1),
CHANSPY_WRITE_VOLADJUST = (1 << 2),
CHANSPY_FORMAT_AUDIO = (1 << 3),
CHANSPY_TRIGGER_MODE = (3 << 4),
CHANSPY_TRIGGER_READ = (1 << 4),
CHANSPY_TRIGGER_WRITE = (2 << 4),
CHANSPY_TRIGGER_NONE = (3 << 4),
CHANSPY_TRIGGER_FLUSH = (1 << 6),
};
struct ast_channel_spy_queue {
AST_LIST_HEAD_NOLOCK(, ast_frame) list;
unsigned int samples;
unsigned int format;
};
struct ast_channel_spy {
AST_LIST_ENTRY(ast_channel_spy) list;
ast_mutex_t lock;
ast_cond_t trigger;
struct ast_channel *chan;
struct ast_channel_spy_queue read_queue;
struct ast_channel_spy_queue write_queue;
unsigned int flags;
enum chanspy_states status;
const char *type;
/* The volume adjustment values are very straightforward:
positive values cause the samples to be multiplied by that amount
negative values cause the samples to be divided by the absolute value of that amount
*/
int read_vol_adjustment;
int write_vol_adjustment;
};
/*!
\brief Adds a spy to a channel, to begin receiving copies of the channel's audio frames.
\param chan The channel to add the spy to.
\param spy A pointer to ast_channel_spy structure describing how the spy is to be used.
\return 0 for success, non-zero for failure
Note: This function performs no locking; you must hold the channel's lock before
calling this function.
*/
int ast_channel_spy_add(struct ast_channel *chan, struct ast_channel_spy *spy);
/*!
\brief Remove a spy from a channel.
\param chan The channel to remove the spy from
\param spy The spy to be removed
\return nothing
Note: This function performs no locking; you must hold the channel's lock before
calling this function.
*/
void ast_channel_spy_remove(struct ast_channel *chan, struct ast_channel_spy *spy);
/*!
\brief Free a spy.
\param spy The spy to free
\return nothing
Note: This function MUST NOT be called with the spy locked.
*/
void ast_channel_spy_free(struct ast_channel_spy *spy);
/*!
\brief Find all spies of a particular type on a channel and stop them.
\param chan The channel to operate on
\param type A character string identifying the type of spies to be stopped
\return nothing
Note: This function performs no locking; you must hold the channel's lock before
calling this function.
*/
void ast_channel_spy_stop_by_type(struct ast_channel *chan, const char *type);
/*!
\brief Read one (or more) frames of audio from a channel being spied upon.
\param spy The spy to operate on
\param samples The number of audio samples to read
\return NULL for failure, one ast_frame pointer, or a chain of ast_frame pointers
This function can return multiple frames if the spy structure needs to be 'flushed'
due to mismatched queue lengths, or if the spy structure is configured to return
unmixed audio (in which case each call to this function will return a frame of audio
from each side of channel).
Note: This function performs no locking; you must hold the spy's lock before calling
this function. You must <b>not</b> hold the channel's lock at the same time.
*/
struct ast_frame *ast_channel_spy_read_frame(struct ast_channel_spy *spy, unsigned int samples);
/*!
\brief Efficiently wait until audio is available for a spy, or an exception occurs.
\param spy The spy to wait on
\return nothing
Note: The locking rules for this function are non-obvious... first, you must <b>not</b>
hold the channel's lock when calling this function. Second, you must hold the spy's lock
before making the function call; while the function runs the lock will be released, and
when the trigger event occurs, the lock will be re-obtained. This means that when control
returns to your code, you will again hold the spy's lock.
*/
void ast_channel_spy_trigger_wait(struct ast_channel_spy *spy);
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
#endif /* _ASTERISK_CHANSPY_H */

View File

@@ -26,7 +26,8 @@ OBJS= io.o sched.o logger.o frame.o loader.o config.o channel.o \
utils.o plc.o jitterbuf.o dnsmgr.o devicestate.o \
netsock.o slinfactory.o ast_expr2.o ast_expr2f.o \
cryptostub.o sha1.o http.o fixedjitterbuf.o abstract_jb.o \
strcompat.o threadstorage.o dial.o astobj2.o global_datastores.o
strcompat.o threadstorage.o dial.o astobj2.o global_datastores.o \
audiohook.o
# we need to link in the objects statically, not as a library, because
# otherwise modules will not have them available if none of the static

626
main/audiohook.c Normal file
View File

@@ -0,0 +1,626 @@
/*
* 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
*
* \author Joshua Colp <jcolp@digium.com>
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/options.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"
struct ast_audiohook_translate {
struct ast_trans_pvt *trans_pvt;
int format;
};
struct ast_audiohook_list {
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;
};
/*! \brief Initialize an audiohook structure
* \param audiohook Audiohook structure
* \param type
* \param source
* \return Returns 0 on success, -1 on failure
*/
int ast_audiohook_init(struct ast_audiohook *audiohook, enum ast_audiohook_type type, const char *source)
{
/* 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);
/* Setup the factories that are needed for this audiohook type */
switch (type) {
case AST_AUDIOHOOK_TYPE_SPY:
ast_slinfactory_init(&audiohook->read_factory);
case AST_AUDIOHOOK_TYPE_WHISPER:
ast_slinfactory_init(&audiohook->write_factory);
break;
default:
break;
}
/* Since we are just starting out... this audiohook is new */
audiohook->status = AST_AUDIOHOOK_STATUS_NEW;
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:
ast_slinfactory_destroy(&audiohook->read_factory);
case AST_AUDIOHOOK_TYPE_WHISPER:
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);
/* Lock and trigger be gone! */
ast_cond_destroy(&audiohook->trigger);
ast_mutex_destroy(&audiohook->lock);
return 0;
}
/*! \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);
/* Write frame out to respective factory */
ast_slinfactory_feed(factory, frame);
/* If we need to notify the respective handler of this audiohook, do so */
switch (ast_test_flag(audiohook, AST_AUDIOHOOK_TRIGGER_MODE)) {
case AST_AUDIOHOOK_TRIGGER_READ:
if (direction == AST_AUDIOHOOK_DIRECTION_READ)
ast_cond_signal(&audiohook->trigger);
break;
case AST_AUDIOHOOK_TRIGGER_WRITE:
if (direction == AST_AUDIOHOOK_DIRECTION_WRITE)
ast_cond_signal(&audiohook->trigger);
break;
default:
break;
}
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,
.subclass = AST_FORMAT_SLINEAR,
.data = buf,
.datalen = sizeof(buf),
.samples = samples,
};
/* Ensure the factory is able to give us the samples we want */
if (samples > ast_slinfactory_available(factory))
return NULL;
/* Read data in from factory */
if (!ast_slinfactory_read(factory, buf, samples))
return NULL;
/* If a volume adjustment needs to be applied apply it */
if (vol)
ast_frame_adjust_volume(&frame, vol);
return ast_frdup(&frame);
}
static struct ast_frame *audiohook_read_frame_both(struct ast_audiohook *audiohook, size_t samples)
{
int i = 0;
short buf1[samples], buf2[samples], *read_buf = NULL, *write_buf = NULL, *final_buf = NULL, *data1 = NULL, *data2 = NULL;
struct ast_frame frame = {
.frametype = AST_FRAME_VOICE,
.subclass = AST_FORMAT_SLINEAR,
.data = NULL,
.datalen = sizeof(buf1),
.samples = samples,
};
/* Start with the read factory... if there are enough samples, read them in */
if (ast_slinfactory_available(&audiohook->read_factory) >= samples) {
if (ast_slinfactory_read(&audiohook->read_factory, buf1, samples)) {
read_buf = buf1;
/* Adjust read volume if need be */
if (audiohook->options.read_volume) {
int count = 0;
short adjust_value = abs(audiohook->options.read_volume);
for (count = 0; count < samples; count++) {
if (audiohook->options.read_volume > 0)
ast_slinear_saturated_multiply(&buf1[count], &adjust_value);
else if (audiohook->options.read_volume < 0)
ast_slinear_saturated_divide(&buf1[count], &adjust_value);
}
}
}
} else if (option_debug)
ast_log(LOG_DEBUG, "Failed to get %zd samples from read factory %p\n", samples, &audiohook->read_factory);
/* Move on to the write factory... if there are enough samples, read them in */
if (ast_slinfactory_available(&audiohook->write_factory) >= samples) {
if (ast_slinfactory_read(&audiohook->write_factory, buf2, samples)) {
write_buf = buf2;
/* Adjust write volume if need be */
if (audiohook->options.write_volume) {
int count = 0;
short adjust_value = abs(audiohook->options.write_volume);
for (count = 0; count < samples; count++) {
if (audiohook->options.write_volume > 0)
ast_slinear_saturated_multiply(&buf2[count], &adjust_value);
else if (audiohook->options.write_volume < 0)
ast_slinear_saturated_divide(&buf2[count], &adjust_value);
}
}
}
} else if (option_debug)
ast_log(LOG_DEBUG, "Failed to get %zd samples from write factory %p\n", samples, &audiohook->write_factory);
/* Basically we figure out which buffer to use... and if mixing can be done here */
if (!read_buf && !write_buf)
return NULL;
else if (read_buf && write_buf) {
for (i = 0, data1 = read_buf, data2 = write_buf; i < samples; i++, data1++, data2++)
ast_slinear_saturated_add(data1, data2);
final_buf = buf1;
} else if (read_buf)
final_buf = buf1;
else if (write_buf)
final_buf = buf2;
/* Make the final buffer part of the frame, so it gets duplicated fine */
frame.data = final_buf;
/* Yahoo, a combined copy of the audio! */
return ast_frdup(&frame);
}
/*! \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
* \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, int format)
{
struct ast_frame *read_frame = NULL, *final_frame = NULL;
if (!(read_frame = (direction == AST_AUDIOHOOK_DIRECTION_BOTH ? audiohook_read_frame_both(audiohook, samples) : audiohook_read_frame_single(audiohook, samples, direction))))
return NULL;
/* If they don't want signed linear back out, we'll have to send it through the translation path */
if (format != AST_FORMAT_SLINEAR) {
/* Rebuild translation path if different format then previously */
if (audiohook->format != format) {
if (audiohook->trans_pvt) {
ast_translator_free_path(audiohook->trans_pvt);
audiohook->trans_pvt = NULL;
}
/* 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 */
if (!(audiohook->trans_pvt = ast_translator_build_path(format, AST_FORMAT_SLINEAR))) {
ast_frfree(read_frame);
return NULL;
}
}
/* 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;
}
/*! \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);
if (!chan->audiohooks) {
/* Whoops... allocate a new structure */
if (!(chan->audiohooks = ast_calloc(1, sizeof(*chan->audiohooks)))) {
ast_channel_unlock(chan);
return -1;
}
AST_LIST_HEAD_INIT_NOLOCK(&chan->audiohooks->spy_list);
AST_LIST_HEAD_INIT_NOLOCK(&chan->audiohooks->whisper_list);
AST_LIST_HEAD_INIT_NOLOCK(&chan->audiohooks->manipulate_list);
}
/* Drop into respective list */
if (audiohook->type == AST_AUDIOHOOK_TYPE_SPY)
AST_LIST_INSERT_TAIL(&chan->audiohooks->spy_list, audiohook, list);
else if (audiohook->type == AST_AUDIOHOOK_TYPE_WHISPER)
AST_LIST_INSERT_TAIL(&chan->audiohooks->whisper_list, audiohook, list);
else if (audiohook->type == AST_AUDIOHOOK_TYPE_MANIPULATE)
AST_LIST_INSERT_TAIL(&chan->audiohooks->manipulate_list, audiohook, list);
/* Change status over to running since it is now attached */
audiohook->status = AST_AUDIOHOOK_STATUS_RUNNING;
ast_channel_unlock(chan);
return 0;
}
/*! \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)
{
if (audiohook->status == AST_AUDIOHOOK_STATUS_DONE)
return 0;
audiohook->status = AST_AUDIOHOOK_STATUS_SHUTDOWN;
while (audiohook->status != AST_AUDIOHOOK_STATUS_DONE)
ast_audiohook_trigger_wait(audiohook);
return 0;
}
/*! \brief Detach audiohooks from list and destroy said list
* \param audiohook_list List of audiohooks
* \return Returns 0 on success, -1 on failure
*/
int ast_audiohook_detach_list(struct ast_audiohook_list *audiohook_list)
{
int i = 0;
struct ast_audiohook *audiohook = NULL;
/* Drop any spies */
AST_LIST_TRAVERSE_SAFE_BEGIN(&audiohook_list->spy_list, audiohook, list) {
ast_audiohook_lock(audiohook);
AST_LIST_REMOVE_CURRENT(&audiohook_list->spy_list, list);
audiohook->status = AST_AUDIOHOOK_STATUS_DONE;
ast_cond_signal(&audiohook->trigger);
ast_audiohook_unlock(audiohook);
}
AST_LIST_TRAVERSE_SAFE_END
/* Drop any whispering sources */
AST_LIST_TRAVERSE_SAFE_BEGIN(&audiohook_list->whisper_list, audiohook, list) {
ast_audiohook_lock(audiohook);
AST_LIST_REMOVE_CURRENT(&audiohook_list->whisper_list, list);
audiohook->status = AST_AUDIOHOOK_STATUS_DONE;
ast_cond_signal(&audiohook->trigger);
ast_audiohook_unlock(audiohook);
}
AST_LIST_TRAVERSE_SAFE_END
/* Drop any manipulaters */
AST_LIST_TRAVERSE_SAFE_BEGIN(&audiohook_list->manipulate_list, audiohook, list) {
ast_audiohook_lock(audiohook);
AST_LIST_REMOVE_CURRENT(&audiohook_list->manipulate_list, list);
audiohook->status = AST_AUDIOHOOK_STATUS_DONE;
ast_audiohook_unlock(audiohook);
audiohook->manipulate_callback(audiohook, NULL, NULL, 0);
}
AST_LIST_TRAVERSE_SAFE_END
/* Drop translation paths if present */
for (i = 0; i < 2; i++) {
if (audiohook_list->in_translate[i].trans_pvt)
ast_translator_free_path(audiohook_list->in_translate[i].trans_pvt);
if (audiohook_list->out_translate[i].trans_pvt)
ast_translator_free_path(audiohook_list->out_translate[i].trans_pvt);
}
/* Free ourselves */
ast_free(audiohook_list);
return 0;
}
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) {
if (!strcasecmp(audiohook->source, source))
return audiohook;
}
AST_LIST_TRAVERSE(&audiohook_list->whisper_list, audiohook, list) {
if (!strcasecmp(audiohook->source, source))
return audiohook;
}
AST_LIST_TRAVERSE(&audiohook_list->manipulate_list, audiohook, list) {
if (!strcasecmp(audiohook->source, source))
return audiohook;
}
return NULL;
}
/*! \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 */
if (!chan->audiohooks) {
ast_channel_unlock(chan);
return -1;
}
audiohook = find_audiohook_by_source(chan->audiohooks, source);
ast_channel_unlock(chan);
if (audiohook && audiohook->status != AST_AUDIOHOOK_STATUS_DONE)
audiohook->status = AST_AUDIOHOOK_STATUS_SHUTDOWN;
return (audiohook ? 0 : -1);
}
/*! \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;
AST_LIST_TRAVERSE_SAFE_BEGIN(&audiohook_list->manipulate_list, audiohook, list) {
ast_audiohook_lock(audiohook);
if (audiohook->status != AST_AUDIOHOOK_STATUS_RUNNING) {
AST_LIST_REMOVE_CURRENT(&audiohook_list->manipulate_list, list);
audiohook->status = AST_AUDIOHOOK_STATUS_DONE;
ast_audiohook_unlock(audiohook);
audiohook->manipulate_callback(audiohook, NULL, NULL, 0);
continue;
}
if (ast_test_flag(audiohook, AST_AUDIOHOOK_WANTS_DTMF))
audiohook->manipulate_callback(audiohook, chan, frame, direction);
ast_audiohook_unlock(audiohook);
}
AST_LIST_TRAVERSE_SAFE_END
return frame;
}
/*! \brief Pass an AUDIO 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 *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_audiohook_translate *in_translate = (direction == AST_AUDIOHOOK_DIRECTION_READ ? &audiohook_list->in_translate[0] : &audiohook_list->in_translate[1]);
struct ast_audiohook_translate *out_translate = (direction == AST_AUDIOHOOK_DIRECTION_READ ? &audiohook_list->out_translate[0] : &audiohook_list->out_translate[1]);
struct ast_frame *start_frame = frame, *middle_frame = frame, *end_frame = frame;
struct ast_audiohook *audiohook = NULL;
int samples = frame->samples;
/* If the frame coming in is not signed linear we have to send it through the in_translate path */
if (frame->subclass != AST_FORMAT_SLINEAR) {
if (in_translate->format != frame->subclass) {
if (in_translate->trans_pvt)
ast_translator_free_path(in_translate->trans_pvt);
if (!(in_translate->trans_pvt = ast_translator_build_path(AST_FORMAT_SLINEAR, frame->subclass)))
return frame;
in_translate->format = frame->subclass;
}
if (!(middle_frame = ast_translate(in_translate->trans_pvt, frame, 0)))
return frame;
}
/* 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) {
AST_LIST_REMOVE_CURRENT(&audiohook_list->spy_list, list);
audiohook->status = AST_AUDIOHOOK_STATUS_DONE;
ast_cond_signal(&audiohook->trigger);
ast_audiohook_unlock(audiohook);
continue;
}
ast_audiohook_write_frame(audiohook, direction, middle_frame);
ast_audiohook_unlock(audiohook);
}
AST_LIST_TRAVERSE_SAFE_END
/* If this frame is being written out to the channel then we need to use whisper sources */
if (direction == AST_AUDIOHOOK_DIRECTION_WRITE && !AST_LIST_EMPTY(&audiohook_list->whisper_list)) {
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) {
ast_audiohook_lock(audiohook);
if (audiohook->status != AST_AUDIOHOOK_STATUS_RUNNING) {
AST_LIST_REMOVE_CURRENT(&audiohook_list->whisper_list, list);
audiohook->status = AST_AUDIOHOOK_STATUS_DONE;
ast_cond_signal(&audiohook->trigger);
ast_audiohook_unlock(audiohook);
continue;
}
if (ast_slinfactory_available(&audiohook->write_factory) >= samples && ast_slinfactory_read(&audiohook->write_factory, read_buf, samples)) {
/* Take audio from this whisper source and combine it into our main buffer */
for (i = 0, data1 = combine_buf, data2 = read_buf; i < samples; i++, data1++, data2++)
ast_slinear_saturated_add(data1, data2);
}
ast_audiohook_unlock(audiohook);
}
AST_LIST_TRAVERSE_SAFE_END
/* We take all of the combined whisper sources and combine them into the audio being written out */
for (i = 0, data1 = middle_frame->data, data2 = combine_buf; i < samples; i++, data1++, data2++)
ast_slinear_saturated_add(data1, data2);
end_frame = middle_frame;
}
/* 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) {
AST_LIST_REMOVE_CURRENT(&audiohook_list->manipulate_list, list);
audiohook->status = AST_AUDIOHOOK_STATUS_DONE;
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);
continue;
}
/* Feed in frame to manipulation */
audiohook->manipulate_callback(audiohook, chan, middle_frame, direction);
ast_audiohook_unlock(audiohook);
}
AST_LIST_TRAVERSE_SAFE_END
end_frame = middle_frame;
}
/* Now we figure out what to do with our end frame (whether to transcode or not) */
if (middle_frame == end_frame) {
/* Middle frame was modified and became the end frame... let's see if we need to transcode */
if (end_frame->subclass != start_frame->subclass) {
if (out_translate->format != start_frame->subclass) {
if (out_translate->trans_pvt)
ast_translator_free_path(out_translate->trans_pvt);
if (!(out_translate->trans_pvt = ast_translator_build_path(start_frame->subclass, AST_FORMAT_SLINEAR))) {
/* We can't transcode this... drop our middle frame and return the original */
ast_frfree(middle_frame);
return start_frame;
}
out_translate->format = start_frame->subclass;
}
/* Transcode from our middle (signed linear) frame to new format of the frame that came in */
if (!(end_frame = ast_translate(out_translate->trans_pvt, middle_frame, 0))) {
/* Failed to transcode the frame... drop it and return the original */
ast_frfree(middle_frame);
return start_frame;
}
/* Here's the scoop... middle frame is no longer of use to us */
ast_frfree(middle_frame);
}
} else {
/* No frame was modified, we can just drop our middle frame and pass the frame we got in out */
ast_frfree(middle_frame);
}
return end_frame;
}
/*! \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 */
if (frame->frametype == AST_FRAME_VOICE)
return audio_audiohook_write_list(chan, audiohook_list, direction, frame);
else if (frame->frametype == AST_FRAME_DTMF)
return dtmf_audiohook_write_list(chan, audiohook_list, direction, frame);
else
return frame;
}
/*! \brief Wait for audiohook trigger to be triggered
* \param audiohook Audiohook to wait on
*/
void ast_audiohook_trigger_wait(struct ast_audiohook *audiohook)
{
struct timeval tv;
struct timespec ts;
tv = ast_tvadd(ast_tvnow(), ast_samp2tv(50000, 1000));
ts.tv_sec = tv.tv_sec;
ts.tv_nsec = tv.tv_usec * 1000;
ast_cond_timedwait(&audiohook->trigger, &audiohook->lock, &ts);
return;
}

View File

@@ -47,7 +47,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/sched.h"
#include "asterisk/options.h"
#include "asterisk/channel.h"
#include "asterisk/chanspy.h"
#include "asterisk/audiohook.h"
#include "asterisk/musiconhold.h"
#include "asterisk/logger.h"
#include "asterisk/say.h"
@@ -70,24 +70,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/threadstorage.h"
#include "asterisk/slinfactory.h"
struct channel_spy_trans {
int last_format;
struct ast_trans_pvt *path;
};
struct ast_channel_spy_list {
struct channel_spy_trans read_translator;
struct channel_spy_trans write_translator;
AST_LIST_HEAD_NOLOCK(, ast_channel_spy) list;
};
struct ast_channel_whisper_buffer {
ast_mutex_t lock;
struct ast_slinfactory sf;
unsigned int original_format;
struct ast_trans_pvt *path;
};
/* uncomment if you have problems with 'monitoring' synchronized files */
#if 0
#define MONITOR_CONSTANT_DELAY
@@ -1233,10 +1215,6 @@ void ast_channel_free(struct ast_channel *chan)
if (chan->music_state)
ast_moh_cleanup(chan);
/* if someone is whispering on the channel, stop them */
if (chan->whisper)
ast_channel_whisper_stop(chan);
/* Free translators */
if (chan->readtrans)
ast_translator_free_path(chan->readtrans);
@@ -1393,176 +1371,6 @@ struct ast_datastore *ast_channel_datastore_find(struct ast_channel *chan, const
return datastore;
}
int ast_channel_spy_add(struct ast_channel *chan, struct ast_channel_spy *spy)
{
/* Link the owner channel to the spy */
spy->chan = chan;
if (!ast_test_flag(spy, CHANSPY_FORMAT_AUDIO)) {
ast_log(LOG_WARNING, "Could not add channel spy '%s' to channel '%s', only audio format spies are supported.\n",
spy->type, chan->name);
return -1;
}
if (ast_test_flag(spy, CHANSPY_READ_VOLADJUST) && (spy->read_queue.format != AST_FORMAT_SLINEAR)) {
ast_log(LOG_WARNING, "Cannot provide volume adjustment on '%s' format spies\n",
ast_getformatname(spy->read_queue.format));
return -1;
}
if (ast_test_flag(spy, CHANSPY_WRITE_VOLADJUST) && (spy->write_queue.format != AST_FORMAT_SLINEAR)) {
ast_log(LOG_WARNING, "Cannot provide volume adjustment on '%s' format spies\n",
ast_getformatname(spy->write_queue.format));
return -1;
}
if (ast_test_flag(spy, CHANSPY_MIXAUDIO) &&
((spy->read_queue.format != AST_FORMAT_SLINEAR) ||
(spy->write_queue.format != AST_FORMAT_SLINEAR))) {
ast_log(LOG_WARNING, "Cannot provide audio mixing on '%s'-'%s' format spies\n",
ast_getformatname(spy->read_queue.format), ast_getformatname(spy->write_queue.format));
return -1;
}
if (!chan->spies) {
if (!(chan->spies = ast_calloc(1, sizeof(*chan->spies)))) {
return -1;
}
AST_LIST_HEAD_INIT_NOLOCK(&chan->spies->list);
AST_LIST_INSERT_HEAD(&chan->spies->list, spy, list);
} else {
AST_LIST_INSERT_TAIL(&chan->spies->list, spy, list);
}
if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE) {
ast_cond_init(&spy->trigger, NULL);
ast_set_flag(spy, CHANSPY_TRIGGER_READ);
ast_clear_flag(spy, CHANSPY_TRIGGER_WRITE);
}
if (option_debug)
ast_log(LOG_DEBUG, "Spy %s added to channel %s\n",
spy->type, chan->name);
return 0;
}
/* Clean up a channel's spy information */
static void spy_cleanup(struct ast_channel *chan)
{
if (!AST_LIST_EMPTY(&chan->spies->list))
return;
if (chan->spies->read_translator.path)
ast_translator_free_path(chan->spies->read_translator.path);
if (chan->spies->write_translator.path)
ast_translator_free_path(chan->spies->write_translator.path);
free(chan->spies);
chan->spies = NULL;
return;
}
/* Detach a spy from it's channel */
static void spy_detach(struct ast_channel_spy *spy, struct ast_channel *chan)
{
/* We only need to poke them if they aren't already done */
if (spy->status != CHANSPY_DONE) {
ast_mutex_lock(&spy->lock);
/* Indicate to the spy to stop */
spy->status = CHANSPY_STOP;
spy->chan = NULL;
/* Poke the spy if needed */
if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE)
ast_cond_signal(&spy->trigger);
if (option_debug)
ast_log(LOG_DEBUG, "Spy %s removed from channel %s\n", spy->type, chan->name);
ast_mutex_unlock(&spy->lock);
}
return;
}
void ast_channel_spy_stop_by_type(struct ast_channel *chan, const char *type)
{
struct ast_channel_spy *spy = NULL;
if (!chan->spies)
return;
AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->spies->list, spy, list) {
if ((spy->type == type) && (spy->status == CHANSPY_RUNNING)) {
AST_LIST_REMOVE_CURRENT(&chan->spies->list, list);
spy_detach(spy, chan);
}
}
AST_LIST_TRAVERSE_SAFE_END
spy_cleanup(chan);
}
void ast_channel_spy_trigger_wait(struct ast_channel_spy *spy)
{
struct timeval tv;
struct timespec ts;
tv = ast_tvadd(ast_tvnow(), ast_samp2tv(50000, 1000));
ts.tv_sec = tv.tv_sec;
ts.tv_nsec = tv.tv_usec * 1000;
ast_cond_timedwait(&spy->trigger, &spy->lock, &ts);
}
void ast_channel_spy_remove(struct ast_channel *chan, struct ast_channel_spy *spy)
{
if (!chan->spies)
return;
AST_LIST_REMOVE(&chan->spies->list, spy, list);
spy_detach(spy, chan);
spy_cleanup(chan);
}
void ast_channel_spy_free(struct ast_channel_spy *spy)
{
struct ast_frame *f = NULL;
if (spy->status == CHANSPY_DONE)
return;
/* Switch status to done in case we get called twice */
spy->status = CHANSPY_DONE;
/* Drop any frames in the queue */
while ((f = AST_LIST_REMOVE_HEAD(&spy->write_queue.list, frame_list)))
ast_frfree(f);
while ((f = AST_LIST_REMOVE_HEAD(&spy->read_queue.list, frame_list)))
ast_frfree(f);
/* Destroy the condition if in use */
if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE)
ast_cond_destroy(&spy->trigger);
/* Destroy our mutex since it is no longer in use */
ast_mutex_destroy(&spy->lock);
return;
}
static void detach_spies(struct ast_channel *chan)
{
struct ast_channel_spy *spy = NULL;
if (!chan->spies)
return;
AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->spies->list, spy, list) {
AST_LIST_REMOVE_CURRENT(&chan->spies->list, list);
spy_detach(spy, chan);
}
AST_LIST_TRAVERSE_SAFE_END
spy_cleanup(chan);
}
/*! \brief Softly hangup a channel, don't lock */
int ast_softhangup_nolock(struct ast_channel *chan, int cause)
{
@@ -1587,129 +1395,6 @@ int ast_softhangup(struct ast_channel *chan, int cause)
return res;
}
enum spy_direction {
SPY_READ,
SPY_WRITE,
};
#define SPY_QUEUE_SAMPLE_LIMIT 4000 /* half of one second */
static void queue_frame_to_spies(struct ast_channel *chan, struct ast_frame *f, enum spy_direction dir)
{
struct ast_frame *translated_frame = NULL;
struct ast_channel_spy *spy;
struct channel_spy_trans *trans;
trans = (dir == SPY_READ) ? &chan->spies->read_translator : &chan->spies->write_translator;
AST_LIST_TRAVERSE(&chan->spies->list, spy, list) {
struct ast_channel_spy_queue *queue;
struct ast_frame *duped_fr;
if (spy->status != CHANSPY_RUNNING)
continue;
ast_mutex_lock(&spy->lock);
queue = (dir == SPY_READ) ? &spy->read_queue : &spy->write_queue;
if ((queue->format == AST_FORMAT_SLINEAR) && (f->subclass != AST_FORMAT_SLINEAR)) {
if (!translated_frame) {
if (trans->path && (trans->last_format != f->subclass)) {
ast_translator_free_path(trans->path);
trans->path = NULL;
}
if (!trans->path) {
if (option_debug)
ast_log(LOG_DEBUG, "Building translator from %s to SLINEAR for spies on channel %s\n",
ast_getformatname(f->subclass), chan->name);
if ((trans->path = ast_translator_build_path(AST_FORMAT_SLINEAR, f->subclass)) == NULL) {
ast_log(LOG_WARNING, "Cannot build a path from %s to %s\n",
ast_getformatname(f->subclass), ast_getformatname(AST_FORMAT_SLINEAR));
ast_mutex_unlock(&spy->lock);
continue;
} else {
trans->last_format = f->subclass;
}
}
if (!(translated_frame = ast_translate(trans->path, f, 0))) {
ast_log(LOG_ERROR, "Translation to %s failed, dropping frame for spies\n",
ast_getformatname(AST_FORMAT_SLINEAR));
ast_mutex_unlock(&spy->lock);
break;
}
}
duped_fr = ast_frdup(translated_frame);
} else if (f->subclass != queue->format) {
ast_log(LOG_WARNING, "Spy '%s' on channel '%s' wants format '%s', but frame is '%s', dropping\n",
spy->type, chan->name,
ast_getformatname(queue->format), ast_getformatname(f->subclass));
ast_mutex_unlock(&spy->lock);
continue;
} else
duped_fr = ast_frdup(f);
AST_LIST_INSERT_TAIL(&queue->list, duped_fr, frame_list);
queue->samples += f->samples;
if (queue->samples > SPY_QUEUE_SAMPLE_LIMIT) {
if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE) {
switch (ast_test_flag(spy, CHANSPY_TRIGGER_MODE)) {
case CHANSPY_TRIGGER_READ:
if (dir == SPY_WRITE) {
ast_set_flag(spy, CHANSPY_TRIGGER_WRITE);
ast_clear_flag(spy, CHANSPY_TRIGGER_READ);
if (option_debug)
ast_log(LOG_DEBUG, "Switching spy '%s' on '%s' to write-trigger mode\n",
spy->type, chan->name);
}
break;
case CHANSPY_TRIGGER_WRITE:
if (dir == SPY_READ) {
ast_set_flag(spy, CHANSPY_TRIGGER_READ);
ast_clear_flag(spy, CHANSPY_TRIGGER_WRITE);
if (option_debug)
ast_log(LOG_DEBUG, "Switching spy '%s' on '%s' to read-trigger mode\n",
spy->type, chan->name);
}
break;
}
if (option_debug)
ast_log(LOG_DEBUG, "Triggering queue flush for spy '%s' on '%s'\n",
spy->type, chan->name);
ast_set_flag(spy, CHANSPY_TRIGGER_FLUSH);
ast_cond_signal(&spy->trigger);
} else {
if (option_debug)
ast_log(LOG_DEBUG, "Spy '%s' on channel '%s' %s queue too long, dropping frames\n",
spy->type, chan->name, (dir == SPY_READ) ? "read" : "write");
while (queue->samples > SPY_QUEUE_SAMPLE_LIMIT) {
struct ast_frame *drop = AST_LIST_REMOVE_HEAD(&queue->list, frame_list);
queue->samples -= drop->samples;
ast_frfree(drop);
}
}
} else {
switch (ast_test_flag(spy, CHANSPY_TRIGGER_MODE)) {
case CHANSPY_TRIGGER_READ:
if (dir == SPY_READ)
ast_cond_signal(&spy->trigger);
break;
case CHANSPY_TRIGGER_WRITE:
if (dir == SPY_WRITE)
ast_cond_signal(&spy->trigger);
break;
}
}
ast_mutex_unlock(&spy->lock);
}
if (translated_frame)
ast_frfree(translated_frame);
}
static void free_translation(struct ast_channel *clone)
{
if (clone->writetrans)
@@ -1732,7 +1417,10 @@ int ast_hangup(struct ast_channel *chan)
if someone is going to masquerade as us */
ast_channel_lock(chan);
detach_spies(chan); /* get rid of spies */
if (chan->audiohooks) {
ast_audiohook_detach_list(chan->audiohooks);
chan->audiohooks = NULL;
}
ast_autoservice_stop(chan);
@@ -2416,6 +2104,12 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
chan->emulate_dtmf_duration = AST_DEFAULT_EMULATE_DTMF_DURATION;
ast_log(LOG_DTMF, "DTMF begin emulation of '%c' with duration %u queued on %s\n", f->subclass, chan->emulate_dtmf_duration, chan->name);
}
if (chan->audiohooks) {
struct ast_frame *old_frame = f;
f = ast_audiohook_write_list(chan, chan->audiohooks, AST_AUDIOHOOK_DIRECTION_READ, f);
if (old_frame != f)
ast_frfree(old_frame);
}
} else {
struct timeval now = ast_tvnow();
if (ast_test_flag(chan, AST_FLAG_IN_DTMF)) {
@@ -2438,6 +2132,12 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
ast_log(LOG_DTMF, "DTMF end passthrough '%c' on %s\n", f->subclass, chan->name);
chan->dtmf_tv = now;
}
if (chan->audiohooks) {
struct ast_frame *old_frame = f;
f = ast_audiohook_write_list(chan, chan->audiohooks, AST_AUDIOHOOK_DIRECTION_READ, f);
if (old_frame != f)
ast_frfree(old_frame);
}
}
break;
case AST_FRAME_DTMF_BEGIN:
@@ -2498,6 +2198,12 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
f->subclass = chan->emulate_dtmf_digit;
f->len = ast_tvdiff_ms(now, chan->dtmf_tv);
chan->dtmf_tv = now;
if (chan->audiohooks) {
struct ast_frame *old_frame = f;
f = ast_audiohook_write_list(chan, chan->audiohooks, AST_AUDIOHOOK_DIRECTION_READ, f);
if (old_frame != f)
ast_frfree(old_frame);
}
ast_log(LOG_DTMF, "DTMF end emulation of '%c' queued on %s\n", f->subclass, chan->name);
} else {
/* Drop voice frames while we're still in the middle of the digit */
@@ -2512,9 +2218,12 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
ast_frfree(f);
f = &ast_null_frame;
} else if ((f->frametype == AST_FRAME_VOICE)) {
if (chan->spies)
queue_frame_to_spies(chan, f, SPY_READ);
if (chan->audiohooks) {
struct ast_frame *old_frame = f;
f = ast_audiohook_write_list(chan, chan->audiohooks, AST_AUDIOHOOK_DIRECTION_READ, f);
if (old_frame != f)
ast_frfree(old_frame);
}
if (chan->monitor && chan->monitor->read_stream ) {
/* XXX what does this do ? */
#ifndef MONITOR_CONSTANT_DELAY
@@ -2809,7 +2518,7 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
{
int res = -1;
int count = 0;
struct ast_frame *f = NULL;
struct ast_frame *f = NULL, *f2 = NULL;
/*Deadlock avoidance*/
while(ast_channel_trylock(chan)) {
@@ -2866,6 +2575,12 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
chan->tech->indicate(chan, fr->subclass, fr->data, fr->datalen);
break;
case AST_FRAME_DTMF_BEGIN:
if (chan->audiohooks) {
struct ast_frame *old_frame = fr;
fr = ast_audiohook_write_list(chan, chan->audiohooks, AST_AUDIOHOOK_DIRECTION_WRITE, fr);
if (old_frame != fr)
f = fr;
}
ast_clear_flag(chan, AST_FLAG_BLOCKING);
ast_channel_unlock(chan);
res = ast_senddigit_begin(chan, fr->subclass);
@@ -2873,6 +2588,12 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
CHECK_BLOCKING(chan);
break;
case AST_FRAME_DTMF_END:
if (chan->audiohooks) {
struct ast_frame *old_frame = fr;
fr = ast_audiohook_write_list(chan, chan->audiohooks, AST_AUDIOHOOK_DIRECTION_WRITE, fr);
if (old_frame != fr)
f = fr;
}
ast_clear_flag(chan, AST_FLAG_BLOCKING);
ast_channel_unlock(chan);
res = ast_senddigit_end(chan, fr->subclass, fr->len);
@@ -2900,42 +2621,25 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
if (chan->tech->write == NULL)
break; /*! \todo XXX should return 0 maybe ? */
/* If someone is whispering on this channel then we must ensure that we are always getting signed linear frames */
if (ast_test_flag(chan, AST_FLAG_WHISPER)) {
if (fr->subclass == AST_FORMAT_SLINEAR)
f = fr;
else {
ast_mutex_lock(&chan->whisper->lock);
if (chan->writeformat != AST_FORMAT_SLINEAR) {
/* Rebuild the translation path and set our write format back to signed linear */
chan->whisper->original_format = chan->writeformat;
ast_set_write_format(chan, AST_FORMAT_SLINEAR);
if (chan->whisper->path)
ast_translator_free_path(chan->whisper->path);
chan->whisper->path = ast_translator_build_path(AST_FORMAT_SLINEAR, chan->whisper->original_format);
}
/* Translate frame using the above translation path */
f = (chan->whisper->path) ? ast_translate(chan->whisper->path, fr, 0) : fr;
ast_mutex_unlock(&chan->whisper->lock);
}
} else {
/* If the frame is in the raw write format, then it's easy... just use the frame - otherwise we will have to translate */
if (fr->subclass == chan->rawwriteformat)
f = fr;
else
f = (chan->writetrans) ? ast_translate(chan->writetrans, fr, 0) : fr;
if (chan->audiohooks) {
struct ast_frame *old_frame = fr;
fr = ast_audiohook_write_list(chan, chan->audiohooks, AST_AUDIOHOOK_DIRECTION_WRITE, fr);
if (old_frame != fr)
f2 = fr;
}
/* If the frame is in the raw write format, then it's easy... just use the frame - otherwise we will have to translate */
if (fr->subclass == chan->rawwriteformat)
f = fr;
else
f = (chan->writetrans) ? ast_translate(chan->writetrans, fr, 0) : fr;
/* If we have no frame of audio, then we have to bail out */
if (f == NULL) {
if (!f) {
res = 0;
break;
}
/* If spies are on the channel then queue the frame out to them */
if (chan->spies)
queue_frame_to_spies(chan, f, SPY_WRITE);
/* If Monitor is running on this channel, then we have to write frames out there too */
if (chan->monitor && chan->monitor->write_stream) {
/* XXX must explain this code */
@@ -2963,29 +2667,6 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
}
}
/* Finally the good part! Write this out to the channel */
if (ast_test_flag(chan, AST_FLAG_WHISPER)) {
/* frame is assumed to be in SLINEAR, since that is
required for whisper mode */
ast_frame_adjust_volume(f, -2);
if (ast_slinfactory_available(&chan->whisper->sf) >= f->samples) {
short buf[f->samples];
struct ast_frame whisper = {
.frametype = AST_FRAME_VOICE,
.subclass = AST_FORMAT_SLINEAR,
.data = buf,
.datalen = sizeof(buf),
.samples = f->samples,
};
ast_mutex_lock(&chan->whisper->lock);
if (ast_slinfactory_read(&chan->whisper->sf, buf, f->samples))
ast_frame_slinear_sum(f, &whisper);
ast_mutex_unlock(&chan->whisper->lock);
}
/* and now put it through the regular translator */
f = (chan->writetrans) ? ast_translate(chan->writetrans, f, 0) : f;
}
if (f)
res = chan->tech->write(chan,f);
else
@@ -3006,6 +2687,8 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
if (f && f != fr)
ast_frfree(f);
if (f2)
ast_frfree(f2);
ast_clear_flag(chan, AST_FLAG_BLOCKING);
/* Consider a write failure to force a soft hangup */
if (res < 0)
@@ -3627,8 +3310,6 @@ int ast_do_masquerade(struct ast_channel *original)
void *t_pvt;
struct ast_callerid tmpcid;
struct ast_channel *clone = original->masq;
struct ast_channel_spy_list *spy_list = NULL;
struct ast_channel_spy *spy = NULL;
struct ast_cdr *cdr;
int rformat = original->readformat;
int wformat = original->writeformat;
@@ -3735,27 +3416,6 @@ int ast_do_masquerade(struct ast_channel *original)
original->rawwriteformat = clone->rawwriteformat;
clone->rawwriteformat = x;
/* Swap the spies */
spy_list = original->spies;
original->spies = clone->spies;
clone->spies = spy_list;
/* Update channel on respective spy lists if present */
if (original->spies) {
AST_LIST_TRAVERSE(&original->spies->list, spy, list) {
ast_mutex_lock(&spy->lock);
spy->chan = original;
ast_mutex_unlock(&spy->lock);
}
}
if (clone->spies) {
AST_LIST_TRAVERSE(&clone->spies->list, spy, list) {
ast_mutex_lock(&spy->lock);
spy->chan = clone;
ast_mutex_unlock(&spy->lock);
}
}
clone->_softhangup = AST_SOFTHANGUP_DEV;
/* And of course, so does our current state. Note we need not
@@ -3800,16 +3460,6 @@ int ast_do_masquerade(struct ast_channel *original)
}
ast_app_group_update(clone, original);
/* move any whisperer over */
ast_channel_whisper_stop(original);
if (ast_test_flag(clone, AST_FLAG_WHISPER)) {
original->whisper = clone->whisper;
ast_set_flag(original, AST_FLAG_WHISPER);
clone->whisper = NULL;
ast_clear_flag(clone, AST_FLAG_WHISPER);
}
/* Move data stores over */
if (AST_LIST_FIRST(&clone->datastores))
AST_LIST_APPEND_LIST(&original->datastores, &clone->datastores, entry);
@@ -4327,7 +3977,7 @@ enum ast_bridge_result ast_channel_bridge(struct ast_channel *c0, struct ast_cha
(config->timelimit == 0) &&
(c0->tech->bridge == c1->tech->bridge) &&
!nativefailed && !c0->monitor && !c1->monitor &&
!c0->spies && !c1->spies && !ast_test_flag(&(config->features_callee),AST_FEATURE_REDIRECT) &&
!c0->audiohooks && !c1->audiohooks && !ast_test_flag(&(config->features_callee),AST_FEATURE_REDIRECT) &&
!ast_test_flag(&(config->features_caller),AST_FEATURE_REDIRECT) &&
!c0->masq && !c0->masqr && !c1->masq && !c1->masqr) {
/* Looks like they share a bridge method and nothing else is in the way */
@@ -4711,129 +4361,6 @@ void ast_set_variables(struct ast_channel *chan, struct ast_variable *vars)
pbx_builtin_setvar_helper(chan, cur->name, cur->value);
}
static void copy_data_from_queue(struct ast_channel_spy_queue *queue, short *buf, unsigned int samples)
{
struct ast_frame *f;
int tocopy;
int bytestocopy;
while (samples) {
if (!(f = AST_LIST_FIRST(&queue->list))) {
ast_log(LOG_ERROR, "Ran out of frames before buffer filled!\n");
break;
}
tocopy = (f->samples > samples) ? samples : f->samples;
bytestocopy = ast_codec_get_len(queue->format, tocopy);
memcpy(buf, f->data, bytestocopy);
samples -= tocopy;
buf += tocopy;
f->samples -= tocopy;
f->data += bytestocopy;
f->datalen -= bytestocopy;
f->offset += bytestocopy;
queue->samples -= tocopy;
if (!f->samples)
ast_frfree(AST_LIST_REMOVE_HEAD(&queue->list, frame_list));
}
}
struct ast_frame *ast_channel_spy_read_frame(struct ast_channel_spy *spy, unsigned int samples)
{
struct ast_frame *result;
/* buffers are allocated to hold SLINEAR, which is the largest format */
short read_buf[samples];
short write_buf[samples];
struct ast_frame *read_frame;
struct ast_frame *write_frame;
int need_dup;
struct ast_frame stack_read_frame = { .frametype = AST_FRAME_VOICE,
.subclass = spy->read_queue.format,
.data = read_buf,
.samples = samples,
.datalen = ast_codec_get_len(spy->read_queue.format, samples),
};
struct ast_frame stack_write_frame = { .frametype = AST_FRAME_VOICE,
.subclass = spy->write_queue.format,
.data = write_buf,
.samples = samples,
.datalen = ast_codec_get_len(spy->write_queue.format, samples),
};
/* if a flush has been requested, dump everything in whichever queue is larger */
if (ast_test_flag(spy, CHANSPY_TRIGGER_FLUSH)) {
if (spy->read_queue.samples > spy->write_queue.samples) {
if (ast_test_flag(spy, CHANSPY_READ_VOLADJUST)) {
AST_LIST_TRAVERSE(&spy->read_queue.list, result, frame_list)
ast_frame_adjust_volume(result, spy->read_vol_adjustment);
}
result = AST_LIST_FIRST(&spy->read_queue.list);
AST_LIST_HEAD_SET_NOLOCK(&spy->read_queue.list, NULL);
spy->read_queue.samples = 0;
} else {
if (ast_test_flag(spy, CHANSPY_WRITE_VOLADJUST)) {
AST_LIST_TRAVERSE(&spy->write_queue.list, result, frame_list)
ast_frame_adjust_volume(result, spy->write_vol_adjustment);
}
result = AST_LIST_FIRST(&spy->write_queue.list);
AST_LIST_HEAD_SET_NOLOCK(&spy->write_queue.list, NULL);
spy->write_queue.samples = 0;
}
ast_clear_flag(spy, CHANSPY_TRIGGER_FLUSH);
return result;
}
if ((spy->read_queue.samples < samples) || (spy->write_queue.samples < samples))
return NULL;
/* short-circuit if both head frames have exactly what we want */
if ((AST_LIST_FIRST(&spy->read_queue.list)->samples == samples) &&
(AST_LIST_FIRST(&spy->write_queue.list)->samples == samples)) {
read_frame = AST_LIST_REMOVE_HEAD(&spy->read_queue.list, frame_list);
write_frame = AST_LIST_REMOVE_HEAD(&spy->write_queue.list, frame_list);
spy->read_queue.samples -= samples;
spy->write_queue.samples -= samples;
need_dup = 0;
} else {
copy_data_from_queue(&spy->read_queue, read_buf, samples);
copy_data_from_queue(&spy->write_queue, write_buf, samples);
read_frame = &stack_read_frame;
write_frame = &stack_write_frame;
need_dup = 1;
}
if (ast_test_flag(spy, CHANSPY_READ_VOLADJUST))
ast_frame_adjust_volume(read_frame, spy->read_vol_adjustment);
if (ast_test_flag(spy, CHANSPY_WRITE_VOLADJUST))
ast_frame_adjust_volume(write_frame, spy->write_vol_adjustment);
if (ast_test_flag(spy, CHANSPY_MIXAUDIO)) {
ast_frame_slinear_sum(read_frame, write_frame);
if (need_dup)
result = ast_frdup(read_frame);
else {
result = read_frame;
ast_frfree(write_frame);
}
} else {
if (need_dup) {
result = ast_frdup(read_frame);
AST_LIST_NEXT(result, frame_list) = ast_frdup(write_frame);
} else {
result = read_frame;
AST_LIST_NEXT(result, frame_list) = write_frame;
}
}
return result;
}
static void *silence_generator_alloc(struct ast_channel *chan, void *data)
{
/* just store the data pointer in the channel structure */
@@ -5094,45 +4621,3 @@ int ast_say_digits_full(struct ast_channel *chan, int num,
return ast_say_digit_str_full(chan, buf, ints, lang, audiofd, ctrlfd);
}
int ast_channel_whisper_start(struct ast_channel *chan)
{
if (chan->whisper)
return -1;
if (!(chan->whisper = ast_calloc(1, sizeof(*chan->whisper))))
return -1;
ast_mutex_init(&chan->whisper->lock);
ast_slinfactory_init(&chan->whisper->sf);
ast_set_flag(chan, AST_FLAG_WHISPER);
return 0;
}
int ast_channel_whisper_feed(struct ast_channel *chan, struct ast_frame *f)
{
if (!chan->whisper)
return -1;
ast_mutex_lock(&chan->whisper->lock);
ast_slinfactory_feed(&chan->whisper->sf, f);
ast_mutex_unlock(&chan->whisper->lock);
return 0;
}
void ast_channel_whisper_stop(struct ast_channel *chan)
{
if (!chan->whisper)
return;
ast_clear_flag(chan, AST_FLAG_WHISPER);
if (chan->whisper->path)
ast_translator_free_path(chan->whisper->path);
if (chan->whisper->original_format && chan->writeformat == AST_FORMAT_SLINEAR)
ast_set_write_format(chan, chan->whisper->original_format);
ast_slinfactory_destroy(&chan->whisper->sf);
ast_mutex_destroy(&chan->whisper->lock);
free(chan->whisper);
chan->whisper = NULL;
}

View File

@@ -2874,7 +2874,7 @@ static enum ast_bridge_result bridge_native_loop(struct ast_channel *c0, struct
if ((c0->tech_pvt != pvt0) ||
(c1->tech_pvt != pvt1) ||
(c0->masq || c0->masqr || c1->masq || c1->masqr) ||
(c0->monitor || c0->spies || c1->monitor || c1->spies)) {
(c0->monitor || c0->audiohooks || c1->monitor || c1->audiohooks)) {
ast_log(LOG_DEBUG, "Oooh, something is weird, backing out\n");
if (c0->tech_pvt == pvt0)
if (pr0->set_rtp_peer(c0, NULL, NULL, 0, 0))
@@ -3158,7 +3158,7 @@ static enum ast_bridge_result bridge_p2p_loop(struct ast_channel *c0, struct ast
if ((c0->tech_pvt != pvt0) ||
(c1->tech_pvt != pvt1) ||
(c0->masq || c0->masqr || c1->masq || c1->masqr) ||
(c0->monitor || c0->spies || c1->monitor || c1->spies)) {
(c0->monitor || c0->audiohooks || c1->monitor || c1->audiohooks)) {
ast_log(LOG_DEBUG, "Oooh, something is weird, backing out\n");
if ((c0->masq || c0->masqr) && (fr = ast_read(c0)))
ast_frfree(fr);