Sat Apr 26 2014 22:01:29

Asterisk developer's documentation


audiohook.c
Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2007, Digium, Inc.
00005  *
00006  * Joshua Colp <jcolp@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Audiohooks Architecture
00022  *
00023  * \author Joshua Colp <jcolp@digium.com>
00024  */
00025 
00026 /*** MODULEINFO
00027    <support_level>core</support_level>
00028  ***/
00029 
00030 #include "asterisk.h"
00031 
00032 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 369013 $")
00033 
00034 #include <signal.h>
00035 
00036 #include "asterisk/channel.h"
00037 #include "asterisk/utils.h"
00038 #include "asterisk/lock.h"
00039 #include "asterisk/linkedlists.h"
00040 #include "asterisk/audiohook.h"
00041 #include "asterisk/slinfactory.h"
00042 #include "asterisk/frame.h"
00043 #include "asterisk/translate.h"
00044 
00045 #define AST_AUDIOHOOK_SYNC_TOLERANCE 100 /*!< Tolerance in milliseconds for audiohooks synchronization */
00046 #define AST_AUDIOHOOK_SMALL_QUEUE_TOLERANCE 100 /*!< When small queue is enabled, this is the maximum amount of audio that can remain queued at a time. */
00047 
00048 struct ast_audiohook_translate {
00049    struct ast_trans_pvt *trans_pvt;
00050    struct ast_format format;
00051 };
00052 
00053 struct ast_audiohook_list {
00054    /* If all the audiohooks in this list are capable
00055     * of processing slinear at any sample rate, this
00056     * variable will be set and the sample rate will
00057     * be preserved during ast_audiohook_write_list()*/
00058    int native_slin_compatible;
00059    int list_internal_samp_rate;/*!< Internal sample rate used when writing to the audiohook list */
00060 
00061    struct ast_audiohook_translate in_translate[2];
00062    struct ast_audiohook_translate out_translate[2];
00063    AST_LIST_HEAD_NOLOCK(, ast_audiohook) spy_list;
00064    AST_LIST_HEAD_NOLOCK(, ast_audiohook) whisper_list;
00065    AST_LIST_HEAD_NOLOCK(, ast_audiohook) manipulate_list;
00066 };
00067 
00068 static int audiohook_set_internal_rate(struct ast_audiohook *audiohook, int rate, int reset)
00069 {
00070    struct ast_format slin;
00071 
00072    if (audiohook->hook_internal_samp_rate == rate) {
00073       return 0;
00074    }
00075 
00076    audiohook->hook_internal_samp_rate = rate;
00077 
00078    ast_format_set(&slin, ast_format_slin_by_rate(rate), 0);
00079    /* Setup the factories that are needed for this audiohook type */
00080    switch (audiohook->type) {
00081    case AST_AUDIOHOOK_TYPE_SPY:
00082       if (reset) {
00083          ast_slinfactory_destroy(&audiohook->read_factory);
00084       }
00085       ast_slinfactory_init_with_format(&audiohook->read_factory, &slin);
00086       /* fall through */
00087    case AST_AUDIOHOOK_TYPE_WHISPER:
00088       if (reset) {
00089          ast_slinfactory_destroy(&audiohook->write_factory);
00090       }
00091       ast_slinfactory_init_with_format(&audiohook->write_factory, &slin);
00092       break;
00093    default:
00094       break;
00095    }
00096    return 0;
00097 }
00098 
00099 /*! \brief Initialize an audiohook structure
00100  * \param audiohook Audiohook structure
00101  * \param type
00102  * \param source
00103  * \return Returns 0 on success, -1 on failure
00104  */
00105 int ast_audiohook_init(struct ast_audiohook *audiohook, enum ast_audiohook_type type, const char *source, enum ast_audiohook_init_flags init_flags)
00106 {
00107    /* Need to keep the type and source */
00108    audiohook->type = type;
00109    audiohook->source = source;
00110 
00111    /* Initialize lock that protects our audiohook */
00112    ast_mutex_init(&audiohook->lock);
00113    ast_cond_init(&audiohook->trigger, NULL);
00114 
00115    audiohook->init_flags = init_flags;
00116 
00117    /* initialize internal rate at 8khz, this will adjust if necessary */
00118    audiohook_set_internal_rate(audiohook, 8000, 0);
00119 
00120    /* Since we are just starting out... this audiohook is new */
00121    ast_audiohook_update_status(audiohook, AST_AUDIOHOOK_STATUS_NEW);
00122 
00123    return 0;
00124 }
00125 
00126 /*! \brief Destroys an audiohook structure
00127  * \param audiohook Audiohook structure
00128  * \return Returns 0 on success, -1 on failure
00129  */
00130 int ast_audiohook_destroy(struct ast_audiohook *audiohook)
00131 {
00132    /* Drop the factories used by this audiohook type */
00133    switch (audiohook->type) {
00134    case AST_AUDIOHOOK_TYPE_SPY:
00135       ast_slinfactory_destroy(&audiohook->read_factory);
00136    case AST_AUDIOHOOK_TYPE_WHISPER:
00137       ast_slinfactory_destroy(&audiohook->write_factory);
00138       break;
00139    default:
00140       break;
00141    }
00142 
00143    /* Destroy translation path if present */
00144    if (audiohook->trans_pvt)
00145       ast_translator_free_path(audiohook->trans_pvt);
00146 
00147    /* Lock and trigger be gone! */
00148    ast_cond_destroy(&audiohook->trigger);
00149    ast_mutex_destroy(&audiohook->lock);
00150 
00151    return 0;
00152 }
00153 
00154 /*! \brief Writes a frame into the audiohook structure
00155  * \param audiohook Audiohook structure
00156  * \param direction Direction the audio frame came from
00157  * \param frame Frame to write in
00158  * \return Returns 0 on success, -1 on failure
00159  */
00160 int ast_audiohook_write_frame(struct ast_audiohook *audiohook, enum ast_audiohook_direction direction, struct ast_frame *frame)
00161 {
00162    struct ast_slinfactory *factory = (direction == AST_AUDIOHOOK_DIRECTION_READ ? &audiohook->read_factory : &audiohook->write_factory);
00163    struct ast_slinfactory *other_factory = (direction == AST_AUDIOHOOK_DIRECTION_READ ? &audiohook->write_factory : &audiohook->read_factory);
00164    struct timeval *rwtime = (direction == AST_AUDIOHOOK_DIRECTION_READ ? &audiohook->read_time : &audiohook->write_time), previous_time = *rwtime;
00165    int our_factory_samples;
00166    int our_factory_ms;
00167    int other_factory_samples;
00168    int other_factory_ms;
00169    int muteme = 0;
00170 
00171    /* Update last feeding time to be current */
00172    *rwtime = ast_tvnow();
00173 
00174    our_factory_samples = ast_slinfactory_available(factory);
00175    our_factory_ms = ast_tvdiff_ms(*rwtime, previous_time) + (our_factory_samples / (audiohook->hook_internal_samp_rate / 1000));
00176    other_factory_samples = ast_slinfactory_available(other_factory);
00177    other_factory_ms = other_factory_samples / (audiohook->hook_internal_samp_rate / 1000);
00178 
00179    if (ast_test_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC) && other_factory_samples && (our_factory_ms - other_factory_ms > AST_AUDIOHOOK_SYNC_TOLERANCE)) {
00180       ast_debug(1, "Flushing audiohook %p so it remains in sync\n", audiohook);
00181       ast_slinfactory_flush(factory);
00182       ast_slinfactory_flush(other_factory);
00183    }
00184 
00185    if (ast_test_flag(audiohook, AST_AUDIOHOOK_SMALL_QUEUE) && ((our_factory_ms > AST_AUDIOHOOK_SMALL_QUEUE_TOLERANCE) || (other_factory_ms > AST_AUDIOHOOK_SMALL_QUEUE_TOLERANCE))) {
00186       ast_debug(1, "Audiohook %p has stale audio in its factories. Flushing them both\n", audiohook);
00187       ast_slinfactory_flush(factory);
00188       ast_slinfactory_flush(other_factory);
00189    }
00190 
00191    /* swap frame data for zeros if mute is required */
00192    if ((ast_test_flag(audiohook, AST_AUDIOHOOK_MUTE_READ) && (direction == AST_AUDIOHOOK_DIRECTION_READ)) ||
00193       (ast_test_flag(audiohook, AST_AUDIOHOOK_MUTE_WRITE) && (direction == AST_AUDIOHOOK_DIRECTION_WRITE)) ||
00194       (ast_test_flag(audiohook, AST_AUDIOHOOK_MUTE_READ | AST_AUDIOHOOK_MUTE_WRITE) == (AST_AUDIOHOOK_MUTE_READ | AST_AUDIOHOOK_MUTE_WRITE))) {
00195          muteme = 1;
00196    }
00197 
00198    if (muteme && frame->datalen > 0) {
00199       ast_frame_clear(frame);
00200    }
00201 
00202    /* Write frame out to respective factory */
00203    ast_slinfactory_feed(factory, frame);
00204 
00205    /* If we need to notify the respective handler of this audiohook, do so */
00206    if ((ast_test_flag(audiohook, AST_AUDIOHOOK_TRIGGER_MODE) == AST_AUDIOHOOK_TRIGGER_READ) && (direction == AST_AUDIOHOOK_DIRECTION_READ)) {
00207       ast_cond_signal(&audiohook->trigger);
00208    } else if ((ast_test_flag(audiohook, AST_AUDIOHOOK_TRIGGER_MODE) == AST_AUDIOHOOK_TRIGGER_WRITE) && (direction == AST_AUDIOHOOK_DIRECTION_WRITE)) {
00209       ast_cond_signal(&audiohook->trigger);
00210    } else if (ast_test_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC)) {
00211       ast_cond_signal(&audiohook->trigger);
00212    }
00213 
00214    return 0;
00215 }
00216 
00217 static struct ast_frame *audiohook_read_frame_single(struct ast_audiohook *audiohook, size_t samples, enum ast_audiohook_direction direction)
00218 {
00219    struct ast_slinfactory *factory = (direction == AST_AUDIOHOOK_DIRECTION_READ ? &audiohook->read_factory : &audiohook->write_factory);
00220    int vol = (direction == AST_AUDIOHOOK_DIRECTION_READ ? audiohook->options.read_volume : audiohook->options.write_volume);
00221    short buf[samples];
00222    struct ast_frame frame = {
00223       .frametype = AST_FRAME_VOICE,
00224       .data.ptr = buf,
00225       .datalen = sizeof(buf),
00226       .samples = samples,
00227    };
00228    ast_format_set(&frame.subclass.format, ast_format_slin_by_rate(audiohook->hook_internal_samp_rate), 0);
00229 
00230    /* Ensure the factory is able to give us the samples we want */
00231    if (samples > ast_slinfactory_available(factory))
00232       return NULL;
00233 
00234    /* Read data in from factory */
00235    if (!ast_slinfactory_read(factory, buf, samples))
00236       return NULL;
00237 
00238    /* If a volume adjustment needs to be applied apply it */
00239    if (vol)
00240       ast_frame_adjust_volume(&frame, vol);
00241 
00242    return ast_frdup(&frame);
00243 }
00244 
00245 static struct ast_frame *audiohook_read_frame_both(struct ast_audiohook *audiohook, size_t samples, struct ast_frame **read_reference, struct ast_frame **write_reference)
00246 {
00247    int i = 0, usable_read, usable_write;
00248    short buf1[samples], buf2[samples], *read_buf = NULL, *write_buf = NULL, *final_buf = NULL, *data1 = NULL, *data2 = NULL;
00249    struct ast_frame frame = {
00250       .frametype = AST_FRAME_VOICE,
00251       .data.ptr = NULL,
00252       .datalen = sizeof(buf1),
00253       .samples = samples,
00254    };
00255    ast_format_set(&frame.subclass.format, ast_format_slin_by_rate(audiohook->hook_internal_samp_rate), 0);
00256 
00257    /* Make sure both factories have the required samples */
00258    usable_read = (ast_slinfactory_available(&audiohook->read_factory) >= samples ? 1 : 0);
00259    usable_write = (ast_slinfactory_available(&audiohook->write_factory) >= samples ? 1 : 0);
00260 
00261    if (!usable_read && !usable_write) {
00262       /* If both factories are unusable bail out */
00263       ast_debug(1, "Read factory %p and write factory %p both fail to provide %zd samples\n", &audiohook->read_factory, &audiohook->write_factory, samples);
00264       return NULL;
00265    }
00266 
00267    /* If we want to provide only a read factory make sure we aren't waiting for other audio */
00268    if (usable_read && !usable_write && (ast_tvdiff_ms(ast_tvnow(), audiohook->write_time) < (samples/8)*2)) {
00269       ast_debug(3, "Write factory %p was pretty quick last time, waiting for them.\n", &audiohook->write_factory);
00270       return NULL;
00271    }
00272 
00273    /* If we want to provide only a write factory make sure we aren't waiting for other audio */
00274    if (usable_write && !usable_read && (ast_tvdiff_ms(ast_tvnow(), audiohook->read_time) < (samples/8)*2)) {
00275       ast_debug(3, "Read factory %p was pretty quick last time, waiting for them.\n", &audiohook->read_factory);
00276       return NULL;
00277    }
00278 
00279    /* Start with the read factory... if there are enough samples, read them in */
00280    if (usable_read) {
00281       if (ast_slinfactory_read(&audiohook->read_factory, buf1, samples)) {
00282          read_buf = buf1;
00283          /* Adjust read volume if need be */
00284          if (audiohook->options.read_volume) {
00285             int count = 0;
00286             short adjust_value = abs(audiohook->options.read_volume);
00287             for (count = 0; count < samples; count++) {
00288                if (audiohook->options.read_volume > 0)
00289                   ast_slinear_saturated_multiply(&buf1[count], &adjust_value);
00290                else if (audiohook->options.read_volume < 0)
00291                   ast_slinear_saturated_divide(&buf1[count], &adjust_value);
00292             }
00293          }
00294       }
00295    } else {
00296       ast_debug(1, "Failed to get %d samples from read factory %p\n", (int)samples, &audiohook->read_factory);
00297    }
00298 
00299    /* Move on to the write factory... if there are enough samples, read them in */
00300    if (usable_write) {
00301       if (ast_slinfactory_read(&audiohook->write_factory, buf2, samples)) {
00302          write_buf = buf2;
00303          /* Adjust write volume if need be */
00304          if (audiohook->options.write_volume) {
00305             int count = 0;
00306             short adjust_value = abs(audiohook->options.write_volume);
00307             for (count = 0; count < samples; count++) {
00308                if (audiohook->options.write_volume > 0)
00309                   ast_slinear_saturated_multiply(&buf2[count], &adjust_value);
00310                else if (audiohook->options.write_volume < 0)
00311                   ast_slinear_saturated_divide(&buf2[count], &adjust_value);
00312             }
00313          }
00314       }
00315    } else {
00316       ast_debug(1, "Failed to get %d samples from write factory %p\n", (int)samples, &audiohook->write_factory);
00317    }
00318 
00319    /* Basically we figure out which buffer to use... and if mixing can be done here */
00320    if (read_buf && read_reference) {
00321       frame.data.ptr = buf1;
00322       *read_reference = ast_frdup(&frame);
00323    }
00324    if (write_buf && write_reference) {
00325       frame.data.ptr = buf2;
00326       *write_reference = ast_frdup(&frame);
00327    }
00328 
00329    if (read_buf && write_buf) {
00330       for (i = 0, data1 = read_buf, data2 = write_buf; i < samples; i++, data1++, data2++) {
00331          ast_slinear_saturated_add(data1, data2);
00332       }
00333       final_buf = buf1;
00334    } else if (read_buf) {
00335       final_buf = buf1;
00336    } else if (write_buf) {
00337       final_buf = buf2;
00338    } else {
00339       return NULL;
00340    }
00341 
00342    /* Make the final buffer part of the frame, so it gets duplicated fine */
00343    frame.data.ptr = final_buf;
00344 
00345    /* Yahoo, a combined copy of the audio! */
00346    return ast_frdup(&frame);
00347 }
00348 
00349 static struct ast_frame *audiohook_read_frame_helper(struct ast_audiohook *audiohook, size_t samples, enum ast_audiohook_direction direction, struct ast_format *format, struct ast_frame **read_reference, struct ast_frame **write_reference)
00350 {
00351    struct ast_frame *read_frame = NULL, *final_frame = NULL;
00352    struct ast_format tmp_fmt;
00353    int samples_converted;
00354 
00355    /* the number of samples requested is based on the format they are requesting.  Inorder
00356     * to process this correctly samples must be converted to our internal sample rate */
00357    if (audiohook->hook_internal_samp_rate == ast_format_rate(format)) {
00358       samples_converted = samples;
00359    } else if (audiohook->hook_internal_samp_rate > ast_format_rate(format)) {
00360       samples_converted = samples * (audiohook->hook_internal_samp_rate / (float) ast_format_rate(format));
00361    } else {
00362       samples_converted = samples * (ast_format_rate(format) / (float) audiohook->hook_internal_samp_rate);
00363    }
00364 
00365    if (!(read_frame = (direction == AST_AUDIOHOOK_DIRECTION_BOTH ?
00366       audiohook_read_frame_both(audiohook, samples_converted, read_reference, write_reference) :
00367       audiohook_read_frame_single(audiohook, samples_converted, direction)))) {
00368       return NULL;
00369    }
00370 
00371    /* If they don't want signed linear back out, we'll have to send it through the translation path */
00372    if (format->id != ast_format_slin_by_rate(audiohook->hook_internal_samp_rate)) {
00373       /* Rebuild translation path if different format then previously */
00374       if (ast_format_cmp(format, &audiohook->format) == AST_FORMAT_CMP_NOT_EQUAL) {
00375          if (audiohook->trans_pvt) {
00376             ast_translator_free_path(audiohook->trans_pvt);
00377             audiohook->trans_pvt = NULL;
00378          }
00379 
00380          /* 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 */
00381          if (!(audiohook->trans_pvt = ast_translator_build_path(format, ast_format_set(&tmp_fmt, ast_format_slin_by_rate(audiohook->hook_internal_samp_rate), 0)))) {
00382             ast_frfree(read_frame);
00383             return NULL;
00384          }
00385          ast_format_copy(&audiohook->format, format);
00386       }
00387       /* Convert to requested format, and allow the read in frame to be freed */
00388       final_frame = ast_translate(audiohook->trans_pvt, read_frame, 1);
00389    } else {
00390       final_frame = read_frame;
00391    }
00392 
00393    return final_frame;
00394 }
00395 
00396 /*! \brief Reads a frame in from the audiohook structure
00397  * \param audiohook Audiohook structure
00398  * \param samples Number of samples wanted in requested output format
00399  * \param direction Direction the audio frame came from
00400  * \param format Format of frame remote side wants back
00401  * \return Returns frame on success, NULL on failure
00402  */
00403 struct ast_frame *ast_audiohook_read_frame(struct ast_audiohook *audiohook, size_t samples, enum ast_audiohook_direction direction, struct ast_format *format)
00404 {
00405    return audiohook_read_frame_helper(audiohook, samples, direction, format, NULL, NULL);
00406 }
00407 
00408 /*! \brief Reads a frame in from the audiohook structure
00409  * \param audiohook Audiohook structure
00410  * \param samples Number of samples wanted
00411  * \param direction Direction the audio frame came from
00412  * \param format Format of frame remote side wants back
00413  * \param read_frame frame pointer for copying read frame data
00414  * \param write_frame frame pointer for copying write frame data
00415  * \return Returns frame on success, NULL on failure
00416  */
00417 struct ast_frame *ast_audiohook_read_frame_all(struct ast_audiohook *audiohook, size_t samples, struct ast_format *format, struct ast_frame **read_frame, struct ast_frame **write_frame)
00418 {
00419    return audiohook_read_frame_helper(audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, format, read_frame, write_frame);
00420 }
00421 
00422 static void audiohook_list_set_samplerate_compatibility(struct ast_audiohook_list *audiohook_list)
00423 {
00424    struct ast_audiohook *ah = NULL;
00425    audiohook_list->native_slin_compatible = 1;
00426    AST_LIST_TRAVERSE(&audiohook_list->manipulate_list, ah, list) {
00427       if (!(ah->init_flags & AST_AUDIOHOOK_MANIPULATE_ALL_RATES)) {
00428          audiohook_list->native_slin_compatible = 0;
00429          return;
00430       }
00431    }
00432 }
00433 
00434 /*! \brief Attach audiohook to channel
00435  * \param chan Channel
00436  * \param audiohook Audiohook structure
00437  * \return Returns 0 on success, -1 on failure
00438  */
00439 int ast_audiohook_attach(struct ast_channel *chan, struct ast_audiohook *audiohook)
00440 {
00441    ast_channel_lock(chan);
00442 
00443    if (!ast_channel_audiohooks(chan)) {
00444       struct ast_audiohook_list *ahlist;
00445       /* Whoops... allocate a new structure */
00446       if (!(ahlist = ast_calloc(1, sizeof(*ahlist)))) {
00447          ast_channel_unlock(chan);
00448          return -1;
00449       }
00450       ast_channel_audiohooks_set(chan, ahlist);
00451       AST_LIST_HEAD_INIT_NOLOCK(&ast_channel_audiohooks(chan)->spy_list);
00452       AST_LIST_HEAD_INIT_NOLOCK(&ast_channel_audiohooks(chan)->whisper_list);
00453       AST_LIST_HEAD_INIT_NOLOCK(&ast_channel_audiohooks(chan)->manipulate_list);
00454       /* This sample rate will adjust as necessary when writing to the list. */
00455       ast_channel_audiohooks(chan)->list_internal_samp_rate = 8000;
00456    }
00457 
00458    /* Drop into respective list */
00459    if (audiohook->type == AST_AUDIOHOOK_TYPE_SPY)
00460       AST_LIST_INSERT_TAIL(&ast_channel_audiohooks(chan)->spy_list, audiohook, list);
00461    else if (audiohook->type == AST_AUDIOHOOK_TYPE_WHISPER)
00462       AST_LIST_INSERT_TAIL(&ast_channel_audiohooks(chan)->whisper_list, audiohook, list);
00463    else if (audiohook->type == AST_AUDIOHOOK_TYPE_MANIPULATE)
00464       AST_LIST_INSERT_TAIL(&ast_channel_audiohooks(chan)->manipulate_list, audiohook, list);
00465 
00466 
00467    audiohook_set_internal_rate(audiohook, ast_channel_audiohooks(chan)->list_internal_samp_rate, 1);
00468    audiohook_list_set_samplerate_compatibility(ast_channel_audiohooks(chan));
00469 
00470    /* Change status over to running since it is now attached */
00471    ast_audiohook_update_status(audiohook, AST_AUDIOHOOK_STATUS_RUNNING);
00472 
00473    ast_channel_unlock(chan);
00474 
00475    return 0;
00476 }
00477 
00478 /*! \brief Update audiohook's status
00479  * \param audiohook Audiohook structure
00480  * \param status Audiohook status enum
00481  *
00482  * \note once status is updated to DONE, this function can not be used to set the
00483  * status back to any other setting.  Setting DONE effectively locks the status as such.
00484  */
00485 
00486 void ast_audiohook_update_status(struct ast_audiohook *audiohook, enum ast_audiohook_status status)
00487 {
00488    ast_audiohook_lock(audiohook);
00489    if (audiohook->status != AST_AUDIOHOOK_STATUS_DONE) {
00490       audiohook->status = status;
00491       ast_cond_signal(&audiohook->trigger);
00492    }
00493    ast_audiohook_unlock(audiohook);
00494 }
00495 
00496 /*! \brief Detach audiohook from channel
00497  * \param audiohook Audiohook structure
00498  * \return Returns 0 on success, -1 on failure
00499  */
00500 int ast_audiohook_detach(struct ast_audiohook *audiohook)
00501 {
00502    if (audiohook->status == AST_AUDIOHOOK_STATUS_NEW || audiohook->status == AST_AUDIOHOOK_STATUS_DONE)
00503       return 0;
00504 
00505    ast_audiohook_update_status(audiohook, AST_AUDIOHOOK_STATUS_SHUTDOWN);
00506 
00507    while (audiohook->status != AST_AUDIOHOOK_STATUS_DONE)
00508       ast_audiohook_trigger_wait(audiohook);
00509 
00510    return 0;
00511 }
00512 
00513 /*! \brief Detach audiohooks from list and destroy said list
00514  * \param audiohook_list List of audiohooks
00515  * \return Returns 0 on success, -1 on failure
00516  */
00517 int ast_audiohook_detach_list(struct ast_audiohook_list *audiohook_list)
00518 {
00519    int i = 0;
00520    struct ast_audiohook *audiohook = NULL;
00521 
00522    /* Drop any spies */
00523    while ((audiohook = AST_LIST_REMOVE_HEAD(&audiohook_list->spy_list, list))) {
00524       ast_audiohook_update_status(audiohook, AST_AUDIOHOOK_STATUS_DONE);
00525    }
00526 
00527    /* Drop any whispering sources */
00528    while ((audiohook = AST_LIST_REMOVE_HEAD(&audiohook_list->whisper_list, list))) {
00529       ast_audiohook_update_status(audiohook, AST_AUDIOHOOK_STATUS_DONE);
00530    }
00531 
00532    /* Drop any manipulaters */
00533    while ((audiohook = AST_LIST_REMOVE_HEAD(&audiohook_list->manipulate_list, list))) {
00534       ast_audiohook_update_status(audiohook, AST_AUDIOHOOK_STATUS_DONE);
00535       audiohook->manipulate_callback(audiohook, NULL, NULL, 0);
00536    }
00537 
00538    /* Drop translation paths if present */
00539    for (i = 0; i < 2; i++) {
00540       if (audiohook_list->in_translate[i].trans_pvt)
00541          ast_translator_free_path(audiohook_list->in_translate[i].trans_pvt);
00542       if (audiohook_list->out_translate[i].trans_pvt)
00543          ast_translator_free_path(audiohook_list->out_translate[i].trans_pvt);
00544    }
00545 
00546    /* Free ourselves */
00547    ast_free(audiohook_list);
00548 
00549    return 0;
00550 }
00551 
00552 /*! \brief find an audiohook based on its source
00553  * \param audiohook_list The list of audiohooks to search in
00554  * \param source The source of the audiohook we wish to find
00555  * \return Return the corresponding audiohook or NULL if it cannot be found.
00556  */
00557 static struct ast_audiohook *find_audiohook_by_source(struct ast_audiohook_list *audiohook_list, const char *source)
00558 {
00559    struct ast_audiohook *audiohook = NULL;
00560 
00561    AST_LIST_TRAVERSE(&audiohook_list->spy_list, audiohook, list) {
00562       if (!strcasecmp(audiohook->source, source))
00563          return audiohook;
00564    }
00565 
00566    AST_LIST_TRAVERSE(&audiohook_list->whisper_list, audiohook, list) {
00567       if (!strcasecmp(audiohook->source, source))
00568          return audiohook;
00569    }
00570 
00571    AST_LIST_TRAVERSE(&audiohook_list->manipulate_list, audiohook, list) {
00572       if (!strcasecmp(audiohook->source, source))
00573          return audiohook;
00574    }
00575 
00576    return NULL;
00577 }
00578 
00579 void ast_audiohook_move_by_source(struct ast_channel *old_chan, struct ast_channel *new_chan, const char *source)
00580 {
00581    struct ast_audiohook *audiohook;
00582    enum ast_audiohook_status oldstatus;
00583 
00584    if (!ast_channel_audiohooks(old_chan) || !(audiohook = find_audiohook_by_source(ast_channel_audiohooks(old_chan), source))) {
00585       return;
00586    }
00587 
00588    /* By locking both channels and the audiohook, we can assure that
00589     * another thread will not have a chance to read the audiohook's status
00590     * as done, even though ast_audiohook_remove signals the trigger
00591     * condition.
00592     */
00593    ast_audiohook_lock(audiohook);
00594    oldstatus = audiohook->status;
00595 
00596    ast_audiohook_remove(old_chan, audiohook);
00597    ast_audiohook_attach(new_chan, audiohook);
00598 
00599    audiohook->status = oldstatus;
00600    ast_audiohook_unlock(audiohook);
00601 }
00602 
00603 /*! \brief Detach specified source audiohook from channel
00604  * \param chan Channel to detach from
00605  * \param source Name of source to detach
00606  * \return Returns 0 on success, -1 on failure
00607  */
00608 int ast_audiohook_detach_source(struct ast_channel *chan, const char *source)
00609 {
00610    struct ast_audiohook *audiohook = NULL;
00611 
00612    ast_channel_lock(chan);
00613 
00614    /* Ensure the channel has audiohooks on it */
00615    if (!ast_channel_audiohooks(chan)) {
00616       ast_channel_unlock(chan);
00617       return -1;
00618    }
00619 
00620    audiohook = find_audiohook_by_source(ast_channel_audiohooks(chan), source);
00621 
00622    ast_channel_unlock(chan);
00623 
00624    if (audiohook && audiohook->status != AST_AUDIOHOOK_STATUS_DONE)
00625       ast_audiohook_update_status(audiohook, AST_AUDIOHOOK_STATUS_SHUTDOWN);
00626 
00627    return (audiohook ? 0 : -1);
00628 }
00629 
00630 /*!
00631  * \brief Remove an audiohook from a specified channel
00632  *
00633  * \param chan Channel to remove from
00634  * \param audiohook Audiohook to remove
00635  *
00636  * \return Returns 0 on success, -1 on failure
00637  *
00638  * \note The channel does not need to be locked before calling this function
00639  */
00640 int ast_audiohook_remove(struct ast_channel *chan, struct ast_audiohook *audiohook)
00641 {
00642    ast_channel_lock(chan);
00643 
00644    if (!ast_channel_audiohooks(chan)) {
00645       ast_channel_unlock(chan);
00646       return -1;
00647    }
00648 
00649    if (audiohook->type == AST_AUDIOHOOK_TYPE_SPY)
00650       AST_LIST_REMOVE(&ast_channel_audiohooks(chan)->spy_list, audiohook, list);
00651    else if (audiohook->type == AST_AUDIOHOOK_TYPE_WHISPER)
00652       AST_LIST_REMOVE(&ast_channel_audiohooks(chan)->whisper_list, audiohook, list);
00653    else if (audiohook->type == AST_AUDIOHOOK_TYPE_MANIPULATE)
00654       AST_LIST_REMOVE(&ast_channel_audiohooks(chan)->manipulate_list, audiohook, list);
00655 
00656    audiohook_list_set_samplerate_compatibility(ast_channel_audiohooks(chan));
00657    ast_audiohook_update_status(audiohook, AST_AUDIOHOOK_STATUS_DONE);
00658 
00659    ast_channel_unlock(chan);
00660 
00661    return 0;
00662 }
00663 
00664 /*! \brief Pass a DTMF frame off to be handled by the audiohook core
00665  * \param chan Channel that the list is coming off of
00666  * \param audiohook_list List of audiohooks
00667  * \param direction Direction frame is coming in from
00668  * \param frame The frame itself
00669  * \return Return frame on success, NULL on failure
00670  */
00671 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)
00672 {
00673    struct ast_audiohook *audiohook = NULL;
00674    int removed = 0;
00675 
00676    AST_LIST_TRAVERSE_SAFE_BEGIN(&audiohook_list->manipulate_list, audiohook, list) {
00677       ast_audiohook_lock(audiohook);
00678       if (audiohook->status != AST_AUDIOHOOK_STATUS_RUNNING) {
00679          AST_LIST_REMOVE_CURRENT(list);
00680          removed = 1;
00681          ast_audiohook_update_status(audiohook, AST_AUDIOHOOK_STATUS_DONE);
00682          ast_audiohook_unlock(audiohook);
00683          audiohook->manipulate_callback(audiohook, NULL, NULL, 0);
00684          continue;
00685       }
00686       if (ast_test_flag(audiohook, AST_AUDIOHOOK_WANTS_DTMF))
00687          audiohook->manipulate_callback(audiohook, chan, frame, direction);
00688       ast_audiohook_unlock(audiohook);
00689    }
00690    AST_LIST_TRAVERSE_SAFE_END;
00691 
00692    /* if an audiohook got removed, reset samplerate compatibility */
00693    if (removed) {
00694       audiohook_list_set_samplerate_compatibility(audiohook_list);
00695    }
00696    return frame;
00697 }
00698 
00699 static struct ast_frame *audiohook_list_translate_to_slin(struct ast_audiohook_list *audiohook_list,
00700    enum ast_audiohook_direction direction, struct ast_frame *frame)
00701 {
00702    struct ast_audiohook_translate *in_translate = (direction == AST_AUDIOHOOK_DIRECTION_READ ?
00703       &audiohook_list->in_translate[0] : &audiohook_list->in_translate[1]);
00704    struct ast_frame *new_frame = frame;
00705    struct ast_format tmp_fmt;
00706    enum ast_format_id slin_id;
00707 
00708    /* If we are capable of maintaining doing samplerates other that 8khz, update
00709     * the internal audiohook_list's rate and higher samplerate audio arrives. By
00710     * updating the list's rate, all the audiohooks in the list will be updated as well
00711     * as the are written and read from. */
00712    if (audiohook_list->native_slin_compatible) {
00713       audiohook_list->list_internal_samp_rate =
00714          MAX(ast_format_rate(&frame->subclass.format), audiohook_list->list_internal_samp_rate);
00715    }
00716 
00717    slin_id = ast_format_slin_by_rate(audiohook_list->list_internal_samp_rate);
00718 
00719    if (frame->subclass.format.id == slin_id) {
00720       return new_frame;
00721    }
00722 
00723    if (ast_format_cmp(&frame->subclass.format, &in_translate->format) == AST_FORMAT_CMP_NOT_EQUAL) {
00724       if (in_translate->trans_pvt) {
00725          ast_translator_free_path(in_translate->trans_pvt);
00726       }
00727       if (!(in_translate->trans_pvt = ast_translator_build_path(ast_format_set(&tmp_fmt, slin_id, 0), &frame->subclass.format))) {
00728          return NULL;
00729       }
00730       ast_format_copy(&in_translate->format, &frame->subclass.format);
00731    }
00732    if (!(new_frame = ast_translate(in_translate->trans_pvt, frame, 0))) {
00733       return NULL;
00734    }
00735 
00736    return new_frame;
00737 }
00738 
00739 static struct ast_frame *audiohook_list_translate_to_native(struct ast_audiohook_list *audiohook_list,
00740    enum ast_audiohook_direction direction, struct ast_frame *slin_frame, struct ast_format *outformat)
00741 {
00742    struct ast_audiohook_translate *out_translate = (direction == AST_AUDIOHOOK_DIRECTION_READ ? &audiohook_list->out_translate[0] : &audiohook_list->out_translate[1]);
00743    struct ast_frame *outframe = NULL;
00744    if (ast_format_cmp(&slin_frame->subclass.format, outformat) == AST_FORMAT_CMP_NOT_EQUAL) {
00745       /* rebuild translators if necessary */
00746       if (ast_format_cmp(&out_translate->format, outformat) == AST_FORMAT_CMP_NOT_EQUAL) {
00747          if (out_translate->trans_pvt) {
00748             ast_translator_free_path(out_translate->trans_pvt);
00749          }
00750          if (!(out_translate->trans_pvt = ast_translator_build_path(outformat, &slin_frame->subclass.format))) {
00751             return NULL;
00752          }
00753          ast_format_copy(&out_translate->format, outformat);
00754       }
00755       /* translate back to the format the frame came in as. */
00756       if (!(outframe = ast_translate(out_translate->trans_pvt, slin_frame, 0))) {
00757          return NULL;
00758       }
00759    }
00760    return outframe;
00761 }
00762 
00763 /*!
00764  * \brief Pass an AUDIO frame off to be handled by the audiohook core
00765  *
00766  * \details
00767  * This function has 3 ast_frames and 3 parts to handle each.  At the beginning of this
00768  * function all 3 frames, start_frame, middle_frame, and end_frame point to the initial
00769  * input frame.
00770  *
00771  * Part_1: Translate the start_frame into SLINEAR audio if it is not already in that
00772  *         format.  The result of this part is middle_frame is guaranteed to be in
00773  *         SLINEAR format for Part_2.
00774  * Part_2: Send middle_frame off to spies and manipulators.  At this point middle_frame is
00775  *         either a new frame as result of the translation, or points directly to the start_frame
00776  *         because no translation to SLINEAR audio was required.
00777  * Part_3: Translate end_frame's audio back into the format of start frame if necessary.  This
00778  *         is only necessary if manipulation of middle_frame occurred.
00779  *
00780  * \param chan Channel that the list is coming off of
00781  * \param audiohook_list List of audiohooks
00782  * \param direction Direction frame is coming in from
00783  * \param frame The frame itself
00784  * \return Return frame on success, NULL on failure
00785  */
00786 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)
00787 {
00788    struct ast_frame *start_frame = frame, *middle_frame = frame, *end_frame = frame;
00789    struct ast_audiohook *audiohook = NULL;
00790    int samples;
00791    int middle_frame_manipulated = 0;
00792    int removed = 0;
00793 
00794    /* ---Part_1. translate start_frame to SLINEAR if necessary. */
00795    if (!(middle_frame = audiohook_list_translate_to_slin(audiohook_list, direction, start_frame))) {
00796       return frame;
00797    }
00798    samples = middle_frame->samples;
00799 
00800    /* ---Part_2: Send middle_frame to spy and manipulator lists.  middle_frame is guaranteed to be SLINEAR here.*/
00801    /* Queue up signed linear frame to each spy */
00802    AST_LIST_TRAVERSE_SAFE_BEGIN(&audiohook_list->spy_list, audiohook, list) {
00803       ast_audiohook_lock(audiohook);
00804       if (audiohook->status != AST_AUDIOHOOK_STATUS_RUNNING) {
00805          AST_LIST_REMOVE_CURRENT(list);
00806          removed = 1;
00807          ast_audiohook_update_status(audiohook, AST_AUDIOHOOK_STATUS_DONE);
00808          ast_audiohook_unlock(audiohook);
00809          continue;
00810       }
00811       audiohook_set_internal_rate(audiohook, audiohook_list->list_internal_samp_rate, 1);
00812       ast_audiohook_write_frame(audiohook, direction, middle_frame);
00813       ast_audiohook_unlock(audiohook);
00814    }
00815    AST_LIST_TRAVERSE_SAFE_END;
00816 
00817    /* If this frame is being written out to the channel then we need to use whisper sources */
00818    if (direction == AST_AUDIOHOOK_DIRECTION_WRITE && !AST_LIST_EMPTY(&audiohook_list->whisper_list)) {
00819       int i = 0;
00820       short read_buf[samples], combine_buf[samples], *data1 = NULL, *data2 = NULL;
00821       memset(&combine_buf, 0, sizeof(combine_buf));
00822       AST_LIST_TRAVERSE_SAFE_BEGIN(&audiohook_list->whisper_list, audiohook, list) {
00823          ast_audiohook_lock(audiohook);
00824          if (audiohook->status != AST_AUDIOHOOK_STATUS_RUNNING) {
00825             AST_LIST_REMOVE_CURRENT(list);
00826             removed = 1;
00827             ast_audiohook_update_status(audiohook, AST_AUDIOHOOK_STATUS_DONE);
00828             ast_audiohook_unlock(audiohook);
00829             continue;
00830          }
00831          audiohook_set_internal_rate(audiohook, audiohook_list->list_internal_samp_rate, 1);
00832          if (ast_slinfactory_available(&audiohook->write_factory) >= samples && ast_slinfactory_read(&audiohook->write_factory, read_buf, samples)) {
00833             /* Take audio from this whisper source and combine it into our main buffer */
00834             for (i = 0, data1 = combine_buf, data2 = read_buf; i < samples; i++, data1++, data2++)
00835                ast_slinear_saturated_add(data1, data2);
00836          }
00837          ast_audiohook_unlock(audiohook);
00838       }
00839       AST_LIST_TRAVERSE_SAFE_END;
00840       /* We take all of the combined whisper sources and combine them into the audio being written out */
00841       for (i = 0, data1 = middle_frame->data.ptr, data2 = combine_buf; i < samples; i++, data1++, data2++) {
00842          ast_slinear_saturated_add(data1, data2);
00843       }
00844       middle_frame_manipulated = 1;
00845    }
00846 
00847    /* Pass off frame to manipulate audiohooks */
00848    if (!AST_LIST_EMPTY(&audiohook_list->manipulate_list)) {
00849       AST_LIST_TRAVERSE_SAFE_BEGIN(&audiohook_list->manipulate_list, audiohook, list) {
00850          ast_audiohook_lock(audiohook);
00851          if (audiohook->status != AST_AUDIOHOOK_STATUS_RUNNING) {
00852             AST_LIST_REMOVE_CURRENT(list);
00853             removed = 1;
00854             ast_audiohook_update_status(audiohook, AST_AUDIOHOOK_STATUS_DONE);
00855             ast_audiohook_unlock(audiohook);
00856             /* We basically drop all of our links to the manipulate audiohook and prod it to do it's own destructive things */
00857             audiohook->manipulate_callback(audiohook, chan, NULL, direction);
00858             continue;
00859          }
00860          audiohook_set_internal_rate(audiohook, audiohook_list->list_internal_samp_rate, 1);
00861          /* Feed in frame to manipulation. */
00862          if (audiohook->manipulate_callback(audiohook, chan, middle_frame, direction)) {
00863             /* XXX IGNORE FAILURE */
00864 
00865             /* If the manipulation fails then the frame will be returned in its original state.
00866              * Since there are potentially more manipulator callbacks in the list, no action should
00867              * be taken here to exit early. */
00868          }
00869          ast_audiohook_unlock(audiohook);
00870       }
00871       AST_LIST_TRAVERSE_SAFE_END;
00872       middle_frame_manipulated = 1;
00873    }
00874 
00875    /* ---Part_3: Decide what to do with the end_frame (whether to transcode or not) */
00876    if (middle_frame_manipulated) {
00877       if (!(end_frame = audiohook_list_translate_to_native(audiohook_list, direction, middle_frame, &start_frame->subclass.format))) {
00878          /* translation failed, so just pass back the input frame */
00879          end_frame = start_frame;
00880       }
00881    } else {
00882       end_frame = start_frame;
00883    }
00884    /* clean up our middle_frame if required */
00885    if (middle_frame != end_frame) {
00886       ast_frfree(middle_frame);
00887       middle_frame = NULL;
00888    }
00889 
00890    /* Before returning, if an audiohook got removed, reset samplerate compatibility */
00891    if (removed) {
00892       audiohook_list_set_samplerate_compatibility(audiohook_list);
00893    }
00894 
00895    return end_frame;
00896 }
00897 
00898 int ast_audiohook_write_list_empty(struct ast_audiohook_list *audiohook_list)
00899 {
00900    if (AST_LIST_EMPTY(&audiohook_list->spy_list) &&
00901       AST_LIST_EMPTY(&audiohook_list->whisper_list) &&
00902       AST_LIST_EMPTY(&audiohook_list->manipulate_list)) {
00903 
00904       return 1;
00905    }
00906    return 0;
00907 }
00908 
00909 /*! \brief Pass a frame off to be handled by the audiohook core
00910  * \param chan Channel that the list is coming off of
00911  * \param audiohook_list List of audiohooks
00912  * \param direction Direction frame is coming in from
00913  * \param frame The frame itself
00914  * \return Return frame on success, NULL on failure
00915  */
00916 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)
00917 {
00918    /* Pass off frame to it's respective list write function */
00919    if (frame->frametype == AST_FRAME_VOICE)
00920       return audio_audiohook_write_list(chan, audiohook_list, direction, frame);
00921    else if (frame->frametype == AST_FRAME_DTMF)
00922       return dtmf_audiohook_write_list(chan, audiohook_list, direction, frame);
00923    else
00924       return frame;
00925 }
00926 
00927 /*! \brief Wait for audiohook trigger to be triggered
00928  * \param audiohook Audiohook to wait on
00929  */
00930 void ast_audiohook_trigger_wait(struct ast_audiohook *audiohook)
00931 {
00932    struct timeval wait;
00933    struct timespec ts;
00934 
00935    wait = ast_tvadd(ast_tvnow(), ast_samp2tv(50000, 1000));
00936    ts.tv_sec = wait.tv_sec;
00937    ts.tv_nsec = wait.tv_usec * 1000;
00938 
00939    ast_cond_timedwait(&audiohook->trigger, &audiohook->lock, &ts);
00940 
00941    return;
00942 }
00943 
00944 /* Count number of channel audiohooks by type, regardless of type */
00945 int ast_channel_audiohook_count_by_source(struct ast_channel *chan, const char *source, enum ast_audiohook_type type)
00946 {
00947    int count = 0;
00948    struct ast_audiohook *ah = NULL;
00949 
00950    if (!ast_channel_audiohooks(chan))
00951       return -1;
00952 
00953    switch (type) {
00954       case AST_AUDIOHOOK_TYPE_SPY:
00955          AST_LIST_TRAVERSE(&ast_channel_audiohooks(chan)->spy_list, ah, list) {
00956             if (!strcmp(ah->source, source)) {
00957                count++;
00958             }
00959          }
00960          break;
00961       case AST_AUDIOHOOK_TYPE_WHISPER:
00962          AST_LIST_TRAVERSE(&ast_channel_audiohooks(chan)->whisper_list, ah, list) {
00963             if (!strcmp(ah->source, source)) {
00964                count++;
00965             }
00966          }
00967          break;
00968       case AST_AUDIOHOOK_TYPE_MANIPULATE:
00969          AST_LIST_TRAVERSE(&ast_channel_audiohooks(chan)->manipulate_list, ah, list) {
00970             if (!strcmp(ah->source, source)) {
00971                count++;
00972             }
00973          }
00974          break;
00975       default:
00976          ast_debug(1, "Invalid audiohook type supplied, (%d)\n", type);
00977          return -1;
00978    }
00979 
00980    return count;
00981 }
00982 
00983 /* Count number of channel audiohooks by type that are running */
00984 int ast_channel_audiohook_count_by_source_running(struct ast_channel *chan, const char *source, enum ast_audiohook_type type)
00985 {
00986    int count = 0;
00987    struct ast_audiohook *ah = NULL;
00988    if (!ast_channel_audiohooks(chan))
00989       return -1;
00990 
00991    switch (type) {
00992       case AST_AUDIOHOOK_TYPE_SPY:
00993          AST_LIST_TRAVERSE(&ast_channel_audiohooks(chan)->spy_list, ah, list) {
00994             if ((!strcmp(ah->source, source)) && (ah->status == AST_AUDIOHOOK_STATUS_RUNNING))
00995                count++;
00996          }
00997          break;
00998       case AST_AUDIOHOOK_TYPE_WHISPER:
00999          AST_LIST_TRAVERSE(&ast_channel_audiohooks(chan)->whisper_list, ah, list) {
01000             if ((!strcmp(ah->source, source)) && (ah->status == AST_AUDIOHOOK_STATUS_RUNNING))
01001                count++;
01002          }
01003          break;
01004       case AST_AUDIOHOOK_TYPE_MANIPULATE:
01005          AST_LIST_TRAVERSE(&ast_channel_audiohooks(chan)->manipulate_list, ah, list) {
01006             if ((!strcmp(ah->source, source)) && (ah->status == AST_AUDIOHOOK_STATUS_RUNNING))
01007                count++;
01008          }
01009          break;
01010       default:
01011          ast_debug(1, "Invalid audiohook type supplied, (%d)\n", type);
01012          return -1;
01013    }
01014    return count;
01015 }
01016 
01017 /*! \brief Audiohook volume adjustment structure */
01018 struct audiohook_volume {
01019    struct ast_audiohook audiohook; /*!< Audiohook attached to the channel */
01020    int read_adjustment;            /*!< Value to adjust frames read from the channel by */
01021    int write_adjustment;           /*!< Value to adjust frames written to the channel by */
01022 };
01023 
01024 /*! \brief Callback used to destroy the audiohook volume datastore
01025  * \param data Volume information structure
01026  * \return Returns nothing
01027  */
01028 static void audiohook_volume_destroy(void *data)
01029 {
01030    struct audiohook_volume *audiohook_volume = data;
01031 
01032    /* Destroy the audiohook as it is no longer in use */
01033    ast_audiohook_destroy(&audiohook_volume->audiohook);
01034 
01035    /* Finally free ourselves, we are of no more use */
01036    ast_free(audiohook_volume);
01037 
01038    return;
01039 }
01040 
01041 /*! \brief Datastore used to store audiohook volume information */
01042 static const struct ast_datastore_info audiohook_volume_datastore = {
01043    .type = "Volume",
01044    .destroy = audiohook_volume_destroy,
01045 };
01046 
01047 /*! \brief Helper function which actually gets called by audiohooks to perform the adjustment
01048  * \param audiohook Audiohook attached to the channel
01049  * \param chan Channel we are attached to
01050  * \param frame Frame of audio we want to manipulate
01051  * \param direction Direction the audio came in from
01052  * \return Returns 0 on success, -1 on failure
01053  */
01054 static int audiohook_volume_callback(struct ast_audiohook *audiohook, struct ast_channel *chan, struct ast_frame *frame, enum ast_audiohook_direction direction)
01055 {
01056    struct ast_datastore *datastore = NULL;
01057    struct audiohook_volume *audiohook_volume = NULL;
01058    int *gain = NULL;
01059 
01060    /* If the audiohook is shutting down don't even bother */
01061    if (audiohook->status == AST_AUDIOHOOK_STATUS_DONE) {
01062       return 0;
01063    }
01064 
01065    /* Try to find the datastore containg adjustment information, if we can't just bail out */
01066    if (!(datastore = ast_channel_datastore_find(chan, &audiohook_volume_datastore, NULL))) {
01067       return 0;
01068    }
01069 
01070    audiohook_volume = datastore->data;
01071 
01072    /* Based on direction grab the appropriate adjustment value */
01073    if (direction == AST_AUDIOHOOK_DIRECTION_READ) {
01074       gain = &audiohook_volume->read_adjustment;
01075    } else if (direction == AST_AUDIOHOOK_DIRECTION_WRITE) {
01076       gain = &audiohook_volume->write_adjustment;
01077    }
01078 
01079    /* If an adjustment value is present modify the frame */
01080    if (gain && *gain) {
01081       ast_frame_adjust_volume(frame, *gain);
01082    }
01083 
01084    return 0;
01085 }
01086 
01087 /*! \brief Helper function which finds and optionally creates an audiohook_volume_datastore datastore on a channel
01088  * \param chan Channel to look on
01089  * \param create Whether to create the datastore if not found
01090  * \return Returns audiohook_volume structure on success, NULL on failure
01091  */
01092 static struct audiohook_volume *audiohook_volume_get(struct ast_channel *chan, int create)
01093 {
01094    struct ast_datastore *datastore = NULL;
01095    struct audiohook_volume *audiohook_volume = NULL;
01096 
01097    /* If we are able to find the datastore return the contents (which is actually an audiohook_volume structure) */
01098    if ((datastore = ast_channel_datastore_find(chan, &audiohook_volume_datastore, NULL))) {
01099       return datastore->data;
01100    }
01101 
01102    /* If we are not allowed to create a datastore or if we fail to create a datastore, bail out now as we have nothing for them */
01103    if (!create || !(datastore = ast_datastore_alloc(&audiohook_volume_datastore, NULL))) {
01104       return NULL;
01105    }
01106 
01107    /* Create a new audiohook_volume structure to contain our adjustments and audiohook */
01108    if (!(audiohook_volume = ast_calloc(1, sizeof(*audiohook_volume)))) {
01109       ast_datastore_free(datastore);
01110       return NULL;
01111    }
01112 
01113    /* Setup our audiohook structure so we can manipulate the audio */
01114    ast_audiohook_init(&audiohook_volume->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "Volume", AST_AUDIOHOOK_MANIPULATE_ALL_RATES);
01115    audiohook_volume->audiohook.manipulate_callback = audiohook_volume_callback;
01116 
01117    /* Attach the audiohook_volume blob to the datastore and attach to the channel */
01118    datastore->data = audiohook_volume;
01119    ast_channel_datastore_add(chan, datastore);
01120 
01121    /* All is well... put the audiohook into motion */
01122    ast_audiohook_attach(chan, &audiohook_volume->audiohook);
01123 
01124    return audiohook_volume;
01125 }
01126 
01127 /*! \brief Adjust the volume on frames read from or written to a channel
01128  * \param chan Channel to muck with
01129  * \param direction Direction to set on
01130  * \param volume Value to adjust the volume by
01131  * \return Returns 0 on success, -1 on failure
01132  */
01133 int ast_audiohook_volume_set(struct ast_channel *chan, enum ast_audiohook_direction direction, int volume)
01134 {
01135    struct audiohook_volume *audiohook_volume = NULL;
01136 
01137    /* Attempt to find the audiohook volume information, but only create it if we are not setting the adjustment value to zero */
01138    if (!(audiohook_volume = audiohook_volume_get(chan, (volume ? 1 : 0)))) {
01139       return -1;
01140    }
01141 
01142    /* Now based on the direction set the proper value */
01143    if (direction == AST_AUDIOHOOK_DIRECTION_READ || direction == AST_AUDIOHOOK_DIRECTION_BOTH) {
01144       audiohook_volume->read_adjustment = volume;
01145    }
01146    if (direction == AST_AUDIOHOOK_DIRECTION_WRITE || direction == AST_AUDIOHOOK_DIRECTION_BOTH) {
01147       audiohook_volume->write_adjustment = volume;
01148    }
01149 
01150    return 0;
01151 }
01152 
01153 /*! \brief Retrieve the volume adjustment value on frames read from or written to a channel
01154  * \param chan Channel to retrieve volume adjustment from
01155  * \param direction Direction to retrieve
01156  * \return Returns adjustment value
01157  */
01158 int ast_audiohook_volume_get(struct ast_channel *chan, enum ast_audiohook_direction direction)
01159 {
01160    struct audiohook_volume *audiohook_volume = NULL;
01161    int adjustment = 0;
01162 
01163    /* Attempt to find the audiohook volume information, but do not create it as we only want to look at the values */
01164    if (!(audiohook_volume = audiohook_volume_get(chan, 0))) {
01165       return 0;
01166    }
01167 
01168    /* Grab the adjustment value based on direction given */
01169    if (direction == AST_AUDIOHOOK_DIRECTION_READ) {
01170       adjustment = audiohook_volume->read_adjustment;
01171    } else if (direction == AST_AUDIOHOOK_DIRECTION_WRITE) {
01172       adjustment = audiohook_volume->write_adjustment;
01173    }
01174 
01175    return adjustment;
01176 }
01177 
01178 /*! \brief Adjust the volume on frames read from or written to a channel
01179  * \param chan Channel to muck with
01180  * \param direction Direction to increase
01181  * \param volume Value to adjust the adjustment by
01182  * \return Returns 0 on success, -1 on failure
01183  */
01184 int ast_audiohook_volume_adjust(struct ast_channel *chan, enum ast_audiohook_direction direction, int volume)
01185 {
01186    struct audiohook_volume *audiohook_volume = NULL;
01187 
01188    /* Attempt to find the audiohook volume information, and create an audiohook if none exists */
01189    if (!(audiohook_volume = audiohook_volume_get(chan, 1))) {
01190       return -1;
01191    }
01192 
01193    /* Based on the direction change the specific adjustment value */
01194    if (direction == AST_AUDIOHOOK_DIRECTION_READ || direction == AST_AUDIOHOOK_DIRECTION_BOTH) {
01195       audiohook_volume->read_adjustment += volume;
01196    }
01197    if (direction == AST_AUDIOHOOK_DIRECTION_WRITE || direction == AST_AUDIOHOOK_DIRECTION_BOTH) {
01198       audiohook_volume->write_adjustment += volume;
01199    }
01200 
01201    return 0;
01202 }
01203 
01204 /*! \brief Mute frames read from or written to a channel
01205  * \param chan Channel to muck with
01206  * \param source Type of audiohook
01207  * \param flag which flag to set / clear
01208  * \param clear set or clear
01209  * \return Returns 0 on success, -1 on failure
01210  */
01211 int ast_audiohook_set_mute(struct ast_channel *chan, const char *source, enum ast_audiohook_flags flag, int clear)
01212 {
01213    struct ast_audiohook *audiohook = NULL;
01214 
01215    ast_channel_lock(chan);
01216 
01217    /* Ensure the channel has audiohooks on it */
01218    if (!ast_channel_audiohooks(chan)) {
01219       ast_channel_unlock(chan);
01220       return -1;
01221    }
01222 
01223    audiohook = find_audiohook_by_source(ast_channel_audiohooks(chan), source);
01224 
01225    if (audiohook) {
01226       if (clear) {
01227          ast_clear_flag(audiohook, flag);
01228       } else {
01229          ast_set_flag(audiohook, flag);
01230       }
01231    }
01232 
01233    ast_channel_unlock(chan);
01234 
01235    return (audiohook ? 0 : -1);
01236 }