Sat Apr 26 2014 22:01:40

Asterisk developer's documentation


res_mutestream.c
Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2009, Olle E. Johansson
00005  *
00006  * Olle E. Johansson <oej@edvina.net>
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 MUTESTREAM audiohooks
00022  *
00023  * \author Olle E. Johansson <oej@edvina.net>
00024  *
00025  *  \ingroup functions
00026  *
00027  * \note This module only handles audio streams today, but can easily be appended to also
00028  * zero out text streams if there's an application for it.
00029  * When we know and understands what happens if we zero out video, we can do that too.
00030  */
00031 
00032 /*** MODULEINFO
00033    <support_level>core</support_level>
00034  ***/
00035 
00036 #include "asterisk.h"
00037 
00038 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 411314 $")
00039 
00040 #include "asterisk/options.h"
00041 #include "asterisk/logger.h"
00042 #include "asterisk/channel.h"
00043 #include "asterisk/module.h"
00044 #include "asterisk/config.h"
00045 #include "asterisk/file.h"
00046 #include "asterisk/pbx.h"
00047 #include "asterisk/frame.h"
00048 #include "asterisk/utils.h"
00049 #include "asterisk/audiohook.h"
00050 #include "asterisk/manager.h"
00051 
00052 /*** DOCUMENTATION
00053    <function name="MUTEAUDIO" language="en_US">
00054       <synopsis>
00055          Muting audio streams in the channel
00056       </synopsis>
00057       <syntax>
00058          <parameter name="direction" required="true">
00059             <para>Must be one of </para>
00060             <enumlist>
00061                <enum name="in">
00062                   <para>Inbound stream (to the PBX)</para>
00063                </enum>
00064                <enum name="out">
00065                   <para>Outbound stream (from the PBX)</para>
00066                </enum>
00067                <enum name="all">
00068                   <para>Both streams</para>
00069                </enum>
00070             </enumlist>
00071          </parameter>
00072       </syntax>
00073       <description>
00074          <para>The MUTEAUDIO function can be used to mute inbound (to the PBX) or outbound audio in a call.
00075          </para>
00076          <para>Examples:
00077          </para>
00078          <para>
00079          MUTEAUDIO(in)=on
00080          </para>
00081          <para>
00082          MUTEAUDIO(in)=off
00083          </para>
00084       </description>
00085    </function>
00086    <manager name="MuteAudio" language="en_US">
00087       <synopsis>
00088          Mute an audio stream.
00089       </synopsis>
00090       <syntax>
00091          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00092          <parameter name="Channel" required="true">
00093             <para>The channel you want to mute.</para>
00094          </parameter>
00095          <parameter name="Direction" required="true">
00096             <enumlist>
00097                <enum name="in">
00098                   <para>Set muting on inbound audio stream. (to the PBX)</para>
00099                </enum>
00100                <enum name="out">
00101                   <para>Set muting on outbound audio stream. (from the PBX)</para>
00102                </enum>
00103                <enum name="all">
00104                   <para>Set muting on inbound and outbound audio streams.</para>
00105                </enum>
00106             </enumlist>
00107          </parameter>
00108          <parameter name="State" required="true">
00109             <enumlist>
00110                <enum name="on">
00111                   <para>Turn muting on.</para>
00112                </enum>
00113                <enum name="off">
00114                   <para>Turn muting off.</para>
00115                </enum>
00116             </enumlist>
00117          </parameter>
00118       </syntax>
00119       <description>
00120          <para>Mute an incoming or outgoing audio stream on a channel.</para>
00121       </description>
00122    </manager>
00123  ***/
00124 
00125 
00126 /*! Our own datastore */
00127 struct mute_information {
00128    struct ast_audiohook audiohook;
00129    int mute_write;
00130    int mute_read;
00131 };
00132 
00133 
00134 /*! Datastore destroy audiohook callback */
00135 static void destroy_callback(void *data)
00136 {
00137    struct mute_information *mute = data;
00138 
00139    /* Destroy the audiohook, and destroy ourselves */
00140    ast_audiohook_destroy(&mute->audiohook);
00141    ast_free(mute);
00142    ast_module_unref(ast_module_info->self);
00143 }
00144 
00145 /*! \brief Static structure for datastore information */
00146 static const struct ast_datastore_info mute_datastore = {
00147    .type = "mute",
00148    .destroy = destroy_callback
00149 };
00150 
00151 /*! \brief The callback from the audiohook subsystem. We basically get a frame to have fun with */
00152 static int mute_callback(struct ast_audiohook *audiohook, struct ast_channel *chan, struct ast_frame *frame, enum ast_audiohook_direction direction)
00153 {
00154    struct ast_datastore *datastore = NULL;
00155    struct mute_information *mute = NULL;
00156 
00157 
00158    /* If the audiohook is stopping it means the channel is shutting down.... but we let the datastore destroy take care of it */
00159    if (audiohook->status == AST_AUDIOHOOK_STATUS_DONE) {
00160       return 0;
00161    }
00162 
00163    ast_channel_lock(chan);
00164    /* Grab datastore which contains our mute information */
00165    if (!(datastore = ast_channel_datastore_find(chan, &mute_datastore, NULL))) {
00166       ast_channel_unlock(chan);
00167       ast_debug(2, "Can't find any datastore to use. Bad. \n");
00168       return 0;
00169    }
00170 
00171    mute = datastore->data;
00172 
00173 
00174    /* If this is audio then allow them to increase/decrease the gains */
00175    if (frame->frametype == AST_FRAME_VOICE) {
00176       ast_debug(2, "Audio frame - direction %s  mute READ %s WRITE %s\n", direction == AST_AUDIOHOOK_DIRECTION_READ ? "read" : "write", mute->mute_read ? "on" : "off", mute->mute_write ? "on" : "off");
00177 
00178       /* Based on direction of frame grab the gain, and confirm it is applicable */
00179       if ((direction == AST_AUDIOHOOK_DIRECTION_READ && mute->mute_read) || (direction == AST_AUDIOHOOK_DIRECTION_WRITE && mute->mute_write)) {
00180          /* Ok, we just want to reset all audio in this frame. Keep NOTHING, thanks. */
00181          ast_frame_clear(frame);
00182       }
00183    }
00184    ast_channel_unlock(chan);
00185 
00186    return 0;
00187 }
00188 
00189 /*! \brief Initialize mute hook on channel, but don't activate it
00190    \pre Assumes that the channel is locked
00191 */
00192 static struct ast_datastore *initialize_mutehook(struct ast_channel *chan)
00193 {
00194    struct ast_datastore *datastore = NULL;
00195    struct mute_information *mute = NULL;
00196 
00197    ast_debug(2, "Initializing new Mute Audiohook \n");
00198 
00199    /* Allocate a new datastore to hold the reference to this mute_datastore and audiohook information */
00200    if (!(datastore = ast_datastore_alloc(&mute_datastore, NULL))) {
00201       return NULL;
00202    }
00203 
00204    if (!(mute = ast_calloc(1, sizeof(*mute)))) {
00205       ast_datastore_free(datastore);
00206       return NULL;
00207    }
00208    ast_audiohook_init(&mute->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "Mute", AST_AUDIOHOOK_MANIPULATE_ALL_RATES);
00209    mute->audiohook.manipulate_callback = mute_callback;
00210    datastore->data = mute;
00211    return datastore;
00212 }
00213 
00214 /*! \brief Add or activate mute audiohook on channel
00215    Assumes channel is locked
00216 */
00217 static int mute_add_audiohook(struct ast_channel *chan, struct mute_information *mute, struct ast_datastore *datastore)
00218 {
00219    /* Activate the settings */
00220    ast_channel_datastore_add(chan, datastore);
00221    if (ast_audiohook_attach(chan, &mute->audiohook)) {
00222       ast_log(LOG_ERROR, "Failed to attach audiohook for muting channel %s\n", ast_channel_name(chan));
00223       return -1;
00224    }
00225    ast_module_ref(ast_module_info->self);
00226    ast_debug(2, "Initialized audiohook on channel %s\n", ast_channel_name(chan));
00227    return 0;
00228 }
00229 
00230 /*! \brief Mute dialplan function */
00231 static int func_mute_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
00232 {
00233    struct ast_datastore *datastore = NULL;
00234    struct mute_information *mute = NULL;
00235    int is_new = 0;
00236    int turnon;
00237 
00238    if (!chan) {
00239       ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
00240       return -1;
00241    }
00242 
00243    ast_channel_lock(chan);
00244    if (!(datastore = ast_channel_datastore_find(chan, &mute_datastore, NULL))) {
00245       if (!(datastore = initialize_mutehook(chan))) {
00246          ast_channel_unlock(chan);
00247          return 0;
00248       }
00249       is_new = 1;
00250    }
00251    mute = datastore->data;
00252 
00253    turnon = ast_true(value);
00254    if (!strcasecmp(data, "out")) {
00255       mute->mute_write = turnon;
00256       ast_debug(1, "%s channel - outbound \n", turnon ? "Muting" : "Unmuting");
00257    } else if (!strcasecmp(data, "in")) {
00258       mute->mute_read = turnon;
00259       ast_debug(1, "%s channel - inbound  \n", turnon ? "Muting" : "Unmuting");
00260    } else if (!strcasecmp(data,"all")) {
00261       mute->mute_write = mute->mute_read = turnon;
00262    }
00263 
00264    if (is_new) {
00265       if (mute_add_audiohook(chan, mute, datastore)) {
00266          /* Can't add audiohook - already printed error message */
00267          ast_datastore_free(datastore);
00268          ast_free(mute);
00269       }
00270    }
00271    ast_channel_unlock(chan);
00272 
00273    return 0;
00274 }
00275 
00276 /* Function for debugging - might be useful */
00277 static struct ast_custom_function mute_function = {
00278    .name = "MUTEAUDIO",
00279    .write = func_mute_write,
00280 };
00281 
00282 static int manager_mutestream(struct mansession *s, const struct message *m)
00283 {
00284    const char *channel = astman_get_header(m, "Channel");
00285    const char *id = astman_get_header(m,"ActionID");
00286    const char *state = astman_get_header(m,"State");
00287    const char *direction = astman_get_header(m,"Direction");
00288    char id_text[256];
00289    struct ast_channel *c = NULL;
00290    struct ast_datastore *datastore = NULL;
00291    struct mute_information *mute = NULL;
00292    int is_new = 0;
00293    int turnon;
00294 
00295    if (ast_strlen_zero(channel)) {
00296       astman_send_error(s, m, "Channel not specified");
00297       return 0;
00298    }
00299    if (ast_strlen_zero(state)) {
00300       astman_send_error(s, m, "State not specified");
00301       return 0;
00302    }
00303    if (ast_strlen_zero(direction)) {
00304       astman_send_error(s, m, "Direction not specified");
00305       return 0;
00306    }
00307    /* Ok, we have everything */
00308 
00309    c = ast_channel_get_by_name(channel);
00310    if (!c) {
00311       astman_send_error(s, m, "No such channel");
00312       return 0;
00313    }
00314 
00315    ast_channel_lock(c);
00316 
00317    if (!(datastore = ast_channel_datastore_find(c, &mute_datastore, NULL))) {
00318       if (!(datastore = initialize_mutehook(c))) {
00319          ast_channel_unlock(c);
00320          ast_channel_unref(c);
00321          astman_send_error(s, m, "Memory allocation failure");
00322          return 0;
00323       }
00324       is_new = 1;
00325    }
00326    mute = datastore->data;
00327 
00328    turnon = ast_true(state);
00329    if (!strcasecmp(direction, "in")) {
00330       mute->mute_read = turnon;
00331    } else if (!strcasecmp(direction, "out")) {
00332       mute->mute_write = turnon;
00333    } else if (!strcasecmp(direction, "all")) {
00334       mute->mute_read = mute->mute_write = turnon;
00335    }
00336 
00337    if (is_new) {
00338       if (mute_add_audiohook(c, mute, datastore)) {
00339          /* Can't add audiohook */
00340          ast_datastore_free(datastore);
00341          ast_free(mute);
00342          ast_channel_unlock(c);
00343          ast_channel_unref(c);
00344          astman_send_error(s, m, "Couldn't add mute audiohook");
00345          return 0;
00346       }
00347    }
00348    ast_channel_unlock(c);
00349    ast_channel_unref(c);
00350 
00351    if (!ast_strlen_zero(id)) {
00352       snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
00353    } else {
00354       id_text[0] = '\0';
00355    }
00356    astman_append(s, "Response: Success\r\n"
00357       "%s"
00358       "\r\n", id_text);
00359    return 0;
00360 }
00361 
00362 
00363 static int load_module(void)
00364 {
00365    int res;
00366 
00367    res = ast_custom_function_register(&mute_function);
00368    res |= ast_manager_register_xml("MuteAudio", EVENT_FLAG_SYSTEM, manager_mutestream);
00369 
00370    return (res ? AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS);
00371 }
00372 
00373 static int unload_module(void)
00374 {
00375    ast_custom_function_unregister(&mute_function);
00376    /* Unregister AMI actions */
00377    ast_manager_unregister("MuteAudio");
00378 
00379    return 0;
00380 }
00381 
00382 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mute audio stream resources");