Sat Feb 11 06:33:05 2012

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

Generated on Sat Feb 11 06:33:05 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6