app_controlplayback.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@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 Trivial application to control playback of a sound file
00022  *
00023  * \author Mark Spencer <markster@digium.com>
00024  *
00025  * \ingroup applications
00026  */
00027 
00028 /*** MODULEINFO
00029    <support_level>core</support_level>
00030  ***/
00031 
00032 #include "asterisk.h"
00033 
00034 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 379830 $")
00035 
00036 #include "asterisk/pbx.h"
00037 #include "asterisk/app.h"
00038 #include "asterisk/module.h"
00039 #include "asterisk/manager.h"
00040 #include "asterisk/utils.h"
00041 #include "asterisk/astobj2.h"
00042 
00043 /*** DOCUMENTATION
00044    <application name="ControlPlayback" language="en_US">
00045       <synopsis>
00046          Play a file with fast forward and rewind.
00047       </synopsis>
00048       <syntax>
00049          <parameter name="filename" required="true" />
00050          <parameter name="skipms">
00051             <para>This is number of milliseconds to skip when rewinding or
00052             fast-forwarding.</para>
00053          </parameter>
00054          <parameter name="ff">
00055             <para>Fast-forward when this DTMF digit is received. (defaults to <literal>#</literal>)</para>
00056          </parameter>
00057          <parameter name="rew">
00058             <para>Rewind when this DTMF digit is received. (defaults to <literal>*</literal>)</para>
00059          </parameter>
00060          <parameter name="stop">
00061             <para>Stop playback when this DTMF digit is received.</para>
00062          </parameter>
00063          <parameter name="pause">
00064             <para>Pause playback when this DTMF digit is received.</para>
00065          </parameter>
00066          <parameter name="restart">
00067             <para>Restart playback when this DTMF digit is received.</para>
00068          </parameter>
00069          <parameter name="options">
00070             <optionlist>
00071                <option name="o">
00072                   <argument name="time" required="true">
00073                      <para>Start at <replaceable>time</replaceable> ms from the
00074                      beginning of the file.</para>
00075                   </argument>
00076                </option>
00077             </optionlist>
00078          </parameter>
00079       </syntax>
00080       <description>
00081          <para>This application will play back the given <replaceable>filename</replaceable>.</para>
00082          <para>It sets the following channel variables upon completion:</para>
00083          <variablelist>
00084             <variable name="CPLAYBACKSTATUS">
00085                <para>Contains the status of the attempt as a text string</para>
00086                <value name="SUCCESS" />
00087                <value name="USERSTOPPED" />
00088                <value name="REMOTESTOPPED" />
00089                <value name="ERROR" />
00090             </variable>
00091             <variable name="CPLAYBACKOFFSET">
00092                <para>Contains the offset in ms into the file where playback
00093                was at when it stopped. <literal>-1</literal> is end of file.</para>
00094             </variable>
00095             <variable name="CPLAYBACKSTOPKEY">
00096                <para>If the playback is stopped by the user this variable contains
00097                the key that was pressed.</para>
00098             </variable>
00099          </variablelist>
00100       </description>
00101    </application>
00102    <manager name="ControlPlayback" language="en_US">
00103       <synopsis>
00104          Control the playback of a file being played to a channel.
00105       </synopsis>
00106       <syntax>
00107          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00108          <parameter name="Channel" required="true">
00109             <para>The name of the channel that currently has a file being played back to it.</para>
00110          </parameter>
00111          <parameter name="Control" required="true">
00112             <enumlist>
00113                <enum  name="stop">
00114                   <para>Stop the playback operation.</para>
00115                </enum>
00116                <enum name="forward">
00117                   <para>Move the current position in the media forward. The amount
00118                   of time that the stream moves forward is determined by the
00119                   <replaceable>skipms</replaceable> value passed to the application
00120                   that initiated the playback.</para>
00121                   <note>
00122                      <para>The default skipms value is <literal>3000</literal> ms.</para>
00123                   </note>
00124                </enum>
00125                <enum name="reverse">
00126                   <para>Move the current position in the media backward. The amount
00127                   of time that the stream moves backward is determined by the
00128                   <replaceable>skipms</replaceable> value passed to the application
00129                   that initiated the playback.</para>
00130                   <note>
00131                      <para>The default skipms value is <literal>3000</literal> ms.</para>
00132                   </note>
00133                </enum>
00134                <enum name="pause">
00135                   <para>Pause/unpause the playback operation, if supported.
00136                   If not supported, stop the playback.</para>
00137                </enum>
00138                <enum name="restart">
00139                   <para>Restart the playback operation, if supported.
00140                   If not supported, stop the playback.</para>
00141                </enum>
00142             </enumlist>
00143          </parameter>
00144       </syntax>
00145       <description>
00146          <para>Control the operation of a media file being played back to a channel.
00147          Note that this AMI action does not initiate playback of media to channel, but
00148          rather controls the operation of a media operation that was already initiated
00149          on the channel.</para>
00150          <note>
00151             <para>The <literal>pause</literal> and <literal>restart</literal>
00152             <replaceable>Control</replaceable> options will stop a playback
00153             operation if that operation was not initiated from the
00154             <replaceable>ControlPlayback</replaceable> application or the
00155             <replaceable>control stream file</replaceable> AGI command.</para>
00156          </note>
00157       </description>
00158       <see-also>
00159          <ref type="application">Playback</ref>
00160          <ref type="application">ControlPlayback</ref>
00161          <ref type="agi">stream file</ref>
00162          <ref type="agi">control stream file</ref>
00163       </see-also>
00164    </manager>
00165  ***/
00166 static const char app[] = "ControlPlayback";
00167 
00168 enum {
00169    OPT_OFFSET = (1 << 1),
00170 };
00171 
00172 enum {
00173    OPT_ARG_OFFSET = 0,
00174    /* must stay as the last entry ... */
00175    OPT_ARG_ARRAY_LEN,
00176 };
00177 
00178 AST_APP_OPTIONS(cpb_opts, BEGIN_OPTIONS
00179    AST_APP_OPTION_ARG('o', OPT_OFFSET, OPT_ARG_OFFSET),
00180    END_OPTIONS
00181 );
00182 
00183 static int is_on_phonepad(char key)
00184 {
00185    return key == 35 || key == 42 || (key >= 48 && key <= 57);
00186 }
00187 
00188 static int is_argument(const char *haystack, int needle)
00189 {
00190    if (ast_strlen_zero(haystack))
00191       return 0;
00192 
00193    if (strchr(haystack, needle))
00194       return -1;
00195 
00196    return 0;
00197 }
00198 
00199 static int controlplayback_exec(struct ast_channel *chan, const char *data)
00200 {
00201    int res = 0;
00202    int skipms = 0;
00203    long offsetms = 0;
00204    char offsetbuf[20];
00205    char stopkeybuf[2];
00206    char *tmp;
00207    struct ast_flags opts = { 0, };
00208    char *opt_args[OPT_ARG_ARRAY_LEN];
00209    AST_DECLARE_APP_ARGS(args,
00210       AST_APP_ARG(filename);
00211       AST_APP_ARG(skip);
00212       AST_APP_ARG(fwd);
00213       AST_APP_ARG(rev);
00214       AST_APP_ARG(stop);
00215       AST_APP_ARG(pause);
00216       AST_APP_ARG(restart);
00217       AST_APP_ARG(options);
00218    );
00219 
00220    if (ast_strlen_zero(data)) {
00221       ast_log(LOG_WARNING, "ControlPlayback requires an argument (filename)\n");
00222       return -1;
00223    }
00224    
00225    tmp = ast_strdupa(data);
00226    AST_STANDARD_APP_ARGS(args, tmp);
00227 
00228    if (args.argc < 1) {
00229       ast_log(LOG_WARNING, "ControlPlayback requires an argument (filename)\n");
00230       return -1;
00231    }
00232 
00233    skipms = args.skip ? (atoi(args.skip) ? atoi(args.skip) : 3000) : 3000;
00234 
00235    if (!args.fwd || !is_on_phonepad(*args.fwd)) {
00236       char *digit = "#";
00237       if (!is_argument(args.rev, *digit) && !is_argument(args.stop, *digit) && !is_argument(args.pause, *digit) && !is_argument(args.restart, *digit))
00238          args.fwd = digit;
00239       else
00240          args.fwd = NULL;
00241    }
00242    if (!args.rev || !is_on_phonepad(*args.rev)) {
00243       char *digit = "*";
00244       if (!is_argument(args.fwd, *digit) && !is_argument(args.stop, *digit) && !is_argument(args.pause, *digit) && !is_argument(args.restart, *digit))
00245          args.rev = digit;
00246       else
00247          args.rev = NULL;
00248    }
00249    ast_debug(1, "Forward key = %s, Rewind key = %s\n", args.fwd, args.rev);
00250    if (args.stop && !is_on_phonepad(*args.stop))
00251       args.stop = NULL;
00252    if (args.pause && !is_on_phonepad(*args.pause))
00253       args.pause = NULL;
00254    if (args.restart && !is_on_phonepad(*args.restart))
00255       args.restart = NULL;
00256 
00257    if (args.options) {
00258       ast_app_parse_options(cpb_opts, &opts, opt_args, args.options);
00259       if (ast_test_flag(&opts, OPT_OFFSET))
00260          offsetms = atol(opt_args[OPT_ARG_OFFSET]);
00261    }
00262 
00263    res = ast_control_streamfile(chan, args.filename, args.fwd, args.rev, args.stop, args.pause, args.restart, skipms, &offsetms);
00264 
00265    /* If we stopped on one of our stop keys, return 0  */
00266    if (res > 0 && args.stop && strchr(args.stop, res)) {
00267       pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "USERSTOPPED");
00268       snprintf(stopkeybuf, sizeof(stopkeybuf), "%c", res);
00269       pbx_builtin_setvar_helper(chan, "CPLAYBACKSTOPKEY", stopkeybuf);
00270       res = 0;
00271    } else if (res > 0 && res == AST_CONTROL_STREAM_STOP) {
00272       pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "REMOTESTOPPED");
00273       res = 0;
00274    } else {
00275       if (res < 0) {
00276          res = 0;
00277          pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "ERROR");
00278       } else
00279          pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "SUCCESS");
00280    }
00281 
00282    snprintf(offsetbuf, sizeof(offsetbuf), "%ld", offsetms);
00283    pbx_builtin_setvar_helper(chan, "CPLAYBACKOFFSET", offsetbuf);
00284 
00285    return res;
00286 }
00287 
00288 static int controlplayback_manager(struct mansession *s, const struct message *m)
00289 {
00290    const char *channel_name = astman_get_header(m, "Channel");
00291    const char *control_type = astman_get_header(m, "Control");
00292    struct ast_channel *chan;
00293 
00294    if (ast_strlen_zero(channel_name)) {
00295       astman_send_error(s, m, "Channel not specified");
00296       return 0;
00297    }
00298 
00299    if (ast_strlen_zero(control_type)) {
00300       astman_send_error(s, m, "Control not specified");
00301       return 0;
00302    }
00303 
00304    chan = ast_channel_get_by_name(channel_name);
00305    if (!chan) {
00306       astman_send_error(s, m, "No such channel");
00307       return 0;
00308    }
00309 
00310    if (!strcasecmp(control_type, "stop")) {
00311       ast_queue_control(chan, AST_CONTROL_STREAM_STOP);
00312    } else if (!strcasecmp(control_type, "forward")) {
00313       ast_queue_control(chan, AST_CONTROL_STREAM_FORWARD);
00314    } else if (!strcasecmp(control_type, "reverse")) {
00315       ast_queue_control(chan, AST_CONTROL_STREAM_REVERSE);
00316    } else if (!strcasecmp(control_type, "pause")) {
00317       ast_queue_control(chan, AST_CONTROL_STREAM_SUSPEND);
00318    } else if (!strcasecmp(control_type, "restart")) {
00319       ast_queue_control(chan, AST_CONTROL_STREAM_RESTART);
00320    } else {
00321       astman_send_error(s, m, "Unknown control type");
00322       chan = ast_channel_unref(chan);
00323       return 0;
00324    }
00325 
00326    chan = ast_channel_unref(chan);
00327    astman_send_ack(s, m, NULL);
00328    return 0;
00329 }
00330 
00331 static int unload_module(void)
00332 {
00333    int res = 0;
00334 
00335    res |= ast_unregister_application(app);
00336    res |= ast_manager_unregister("ControlPlayback");
00337 
00338    return res;
00339 }
00340 
00341 static int load_module(void)
00342 {
00343    int res = 0;
00344 
00345    res |= ast_register_application_xml(app, controlplayback_exec);
00346    res |= ast_manager_register_xml("ControlPlayback", EVENT_FLAG_CALL, controlplayback_manager);
00347 
00348    return res;
00349 }
00350 
00351 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Control Playback Application");

Generated on Thu Apr 16 06:27:08 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6