Index: channel.c =================================================================== --- channel.c (revision 63151) +++ channel.c (working copy) @@ -71,6 +71,7 @@ #include "asterisk/app.h" #include "asterisk/transcap.h" #include "asterisk/devicestate.h" +#include "asterisk/slinfactory.h" struct channel_spy_trans { int last_format; @@ -83,6 +84,13 @@ 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 @@ -924,6 +932,10 @@ 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 translatosr */ if (chan->readtrans) ast_translator_free_path(chan->readtrans); @@ -2311,7 +2323,31 @@ break; default: if (chan->tech->write) { - f = (chan->writetrans) ? ast_translate(chan->writetrans, fr, 0) : fr; + /* 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 (f) { if (f->frametype == AST_FRAME_VOICE && chan->spies) queue_frame_to_spies(chan, f, SPY_WRITE); @@ -2339,8 +2375,33 @@ if (ast_writestream(chan->monitor->write_stream, f) < 0) ast_log(LOG_WARNING, "Failed to write data to channel monitor write stream\n"); } + /* Finally the good part! Write this out to the channel */ + if (f->frametype == AST_FRAME_VOICE && 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, + }; - res = chan->tech->write(chan, f); + 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 + res = 0; } else res = 0; } @@ -3107,6 +3168,14 @@ } /* Drop group from original */ ast_app_group_discard(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); + } clone_variables(original, clone); AST_LIST_HEAD_INIT_NOLOCK(&clone->varshead); /* Presense of ADSI capable CPE follows clone */ @@ -4174,3 +4243,47 @@ free(state); } + +int ast_channel_whisper_start(struct ast_channel *chan) +{ + if (chan->whisper) + return -1; + + if (!(chan->whisper = 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 || !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; +} + Index: apps/app_chanspy.c =================================================================== --- apps/app_chanspy.c (revision 63151) +++ apps/app_chanspy.c (working copy) @@ -53,7 +53,7 @@ #define ALL_DONE(u, ret) LOCAL_USER_REMOVE(u); return ret; #define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0 -static const char *synopsis = "Listen to the audio of an active channel"; +static const char *synopsis = "Listen to a channel, and optionally whisper into it"; static const char *app = "ChanSpy"; static const char *desc = " ChanSpy([chanprefix][|options]): This application is used to listen to the\n" @@ -71,12 +71,18 @@ " b - Only spy on channels involved in a bridged call.\n" " g(grp) - Match only channels where their ${SPYGROUP} variable is set to\n" " 'grp'.\n" -" q - Don't play a beep when beginning to spy on a channel.\n" +" q - Don't play a beep when beginning to spy on a channel, or speak the\n" +" selected channel name.\n" " r[(basename)] - Record the session to the monitor spool directory. An\n" " optional base for the filename may be specified. The\n" " default is 'chanspy'.\n" " v([value]) - Adjust the initial volume in the range from -4 to 4. A\n" " negative value refers to a quieter setting.\n" +" w - Enable 'whisper' mode, so the spying channel can talk to\n" +" the spied-on channel.\n" +" W - Enable 'private whisper' mode, so the spying channel can\n" +" talk to the spied-on channel but cannot listen to that\n" +" channel.\n" ; static const char *chanspy_spy_type = "ChanSpy"; @@ -87,6 +93,8 @@ OPTION_VOLUME = (1 << 2), /* Specify initial volume */ OPTION_GROUP = (1 << 3), /* Only look at channels in group */ OPTION_RECORD = (1 << 4), /* Record */ + OPTION_WHISPER = (1 << 5), + OPTION_PRIVATE = (1 << 6), /* Private Whisper mode */ } chanspy_opt_flags; enum { @@ -99,6 +107,8 @@ AST_APP_OPTIONS(chanspy_opts, { AST_APP_OPTION('q', OPTION_QUIET), AST_APP_OPTION('b', OPTION_BRIDGED), + AST_APP_OPTION('w', OPTION_WHISPER), + AST_APP_OPTION('W', OPTION_PRIVATE), AST_APP_OPTION_ARG('v', OPTION_VOLUME, OPT_ARG_VOLUME), AST_APP_OPTION_ARG('g', OPTION_GROUP, OPT_ARG_GROUP), AST_APP_OPTION_ARG('r', OPTION_RECORD, OPT_ARG_RECORD), @@ -234,12 +244,14 @@ csth->volfactor = 0; } -static int channel_spy(struct ast_channel *chan, struct ast_channel *spyee, int *volfactor, int fd) +static int channel_spy(struct ast_channel *chan, struct ast_channel *spyee, int *volfactor, int fd, + const struct ast_flags *flags) { struct chanspy_translation_helper csth; int running = 0, res = 0, x = 0; char inp[24] = "", *name = NULL; struct ast_frame *f = NULL; + struct ast_silence_generator *silgen = NULL; if ((chan && ast_check_hangup(chan)) || (spyee && ast_check_hangup(spyee))) return 0; @@ -272,8 +284,31 @@ 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_activate_generator(chan, &spygen, &csth); + if (ast_test_flag(flags, OPTION_PRIVATE)) + silgen = ast_channel_start_silence_generator(chan); + else + ast_activate_generator(chan, &spygen, &csth); + while (csth.spy.status == CHANSPY_RUNNING && (res = ast_waitfor(chan, -1) > -1)) { @@ -281,6 +316,12 @@ if (!(f = ast_read(chan))) break; + if (ast_test_flag(flags, OPTION_WHISPER) && + (f->frametype == AST_FRAME_VOICE)) { + ast_channel_whisper_feed(csth.spy.chan, f); + ast_frfree(f); + continue; + } /* Now if this is DTMF then we have to handle it as such, otherwise just skip it */ res = 0; if (f->frametype == AST_FRAME_DTMF) @@ -328,7 +369,14 @@ } ast_deactivate_generator(chan); + 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); + ast_mutex_lock(&csth.spy.lock); if (csth.spy.chan) { csth.spy.status = CHANSPY_DONE; @@ -430,6 +478,8 @@ else volfactor = vol; } + if (ast_test_flag(&flags, OPTION_PRIVATE)) + ast_set_flag(&flags, OPTION_WHISPER); } if (recbase) { @@ -508,7 +558,7 @@ } count++; prev = peer; - res = channel_spy(chan, peer, &volfactor, fd); + res = channel_spy(chan, peer, &volfactor, fd, &flags); if (res == -1) { break; } else if (res > 1 && spec) { Index: slinfactory.c =================================================================== --- slinfactory.c (revision 63151) +++ slinfactory.c (working copy) @@ -157,5 +157,7 @@ } - - +unsigned int ast_slinfactory_available(const struct ast_slinfactory *sf) +{ + return sf->size; +} Index: include/asterisk/channel.h =================================================================== --- include/asterisk/channel.h (revision 63151) +++ include/asterisk/channel.h (working copy) @@ -245,6 +245,7 @@ }; struct ast_channel_spy_list; +struct ast_channel_whisper_buffer; /*! Main Channel structure associated with a channel. * This is the side of it mostly used by the pbx and call management. @@ -411,6 +412,8 @@ /*! Chan Spy stuff */ struct ast_channel_spy_list *spies; + /*!< Whisper Paging buffer */ + struct ast_channel_whisper_buffer *whisper; /*! For easy linking */ struct ast_channel *next; @@ -436,6 +439,7 @@ finding the next priority to run */ #define AST_FLAG_NOTNEW (1 << 10) /*!< see bug:7855 incorrect Newchannel event generation */ +#define AST_FLAG_WHISPER (1 << 11) /*!< Is this channel being whispered on */ /* @} */ #define AST_FEATURE_PLAY_WARNING (1 << 0) @@ -1201,7 +1205,39 @@ /* print call- and pickup groups into buffer */ extern char *ast_print_group(char *buf, int buflen, ast_group_t group); +/*! + \brief Begin 'whispering' onto a channel + \param chan The channel to whisper onto + \return 0 for success, non-zero for failure + + This function will add a whisper buffer onto a channel and set a flag + causing writes to the channel to reduce the volume level of the written + audio samples, and then to mix in audio from the whisper buffer if it + is available. + Note: This function performs no locking; you must hold the channel's lock before + calling this function. + */ +int ast_channel_whisper_start(struct ast_channel *chan); + +/*! + \brief Feed an audio frame into the whisper buffer on a channel + \param chan The channel to whisper onto + \return 0 for success, non-zero for failure + */ +int ast_channel_whisper_feed(struct ast_channel *chan, struct ast_frame *f); + +/*! + \brief Stop 'whispering' onto a channel + \param chan The channel to whisper onto + \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. + */ +void ast_channel_whisper_stop(struct ast_channel *chan); + + #if defined(__cplusplus) || defined(c_plusplus) } #endif Index: include/asterisk/slinfactory.h =================================================================== --- include/asterisk/slinfactory.h (revision 63151) +++ include/asterisk/slinfactory.h (working copy) @@ -46,9 +46,8 @@ void ast_slinfactory_destroy(struct ast_slinfactory *sf); int ast_slinfactory_feed(struct ast_slinfactory *sf, struct ast_frame *f); int ast_slinfactory_read(struct ast_slinfactory *sf, short *buf, size_t bytes); - +unsigned int ast_slinfactory_available(const struct ast_slinfactory *sf); - #if defined(__cplusplus) || defined(c_plusplus) } #endif