Asterisk - The Open Source Telephony Project  18.5.0
app_controlplayback.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <[email protected]>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 
19 /*! \file
20  *
21  * \brief Trivial application to control playback of a sound file
22  *
23  * \author Mark Spencer <[email protected]>
24  *
25  * \ingroup applications
26  */
27 
28 /*** MODULEINFO
29  <support_level>core</support_level>
30  ***/
31 
32 #include "asterisk.h"
33 
34 #include "asterisk/pbx.h"
35 #include "asterisk/app.h"
36 #include "asterisk/module.h"
37 #include "asterisk/manager.h"
38 #include "asterisk/utils.h"
39 #include "asterisk/astobj2.h"
40 
41 /*** DOCUMENTATION
42  <application name="ControlPlayback" language="en_US">
43  <synopsis>
44  Play a file with fast forward and rewind.
45  </synopsis>
46  <syntax>
47  <parameter name="filename" required="true" />
48  <parameter name="skipms">
49  <para>This is number of milliseconds to skip when rewinding or
50  fast-forwarding.</para>
51  </parameter>
52  <parameter name="ff">
53  <para>Fast-forward when this DTMF digit is received. (defaults to <literal>#</literal>)</para>
54  </parameter>
55  <parameter name="rew">
56  <para>Rewind when this DTMF digit is received. (defaults to <literal>*</literal>)</para>
57  </parameter>
58  <parameter name="stop">
59  <para>Stop playback when this DTMF digit is received.</para>
60  </parameter>
61  <parameter name="pause">
62  <para>Pause playback when this DTMF digit is received.</para>
63  </parameter>
64  <parameter name="restart">
65  <para>Restart playback when this DTMF digit is received.</para>
66  </parameter>
67  <parameter name="options">
68  <optionlist>
69  <option name="o">
70  <argument name="time" required="true">
71  <para>Start at <replaceable>time</replaceable> ms from the
72  beginning of the file.</para>
73  </argument>
74  </option>
75  </optionlist>
76  </parameter>
77  </syntax>
78  <description>
79  <para>This application will play back the given <replaceable>filename</replaceable>.</para>
80  <para>It sets the following channel variables upon completion:</para>
81  <variablelist>
82  <variable name="CPLAYBACKSTATUS">
83  <para>Contains the status of the attempt as a text string</para>
84  <value name="SUCCESS" />
85  <value name="USERSTOPPED" />
86  <value name="REMOTESTOPPED" />
87  <value name="ERROR" />
88  </variable>
89  <variable name="CPLAYBACKOFFSET">
90  <para>Contains the offset in ms into the file where playback
91  was at when it stopped. <literal>-1</literal> is end of file.</para>
92  </variable>
93  <variable name="CPLAYBACKSTOPKEY">
94  <para>If the playback is stopped by the user this variable contains
95  the key that was pressed.</para>
96  </variable>
97  </variablelist>
98  </description>
99  </application>
100  <manager name="ControlPlayback" language="en_US">
101  <synopsis>
102  Control the playback of a file being played to a channel.
103  </synopsis>
104  <syntax>
105  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
106  <parameter name="Channel" required="true">
107  <para>The name of the channel that currently has a file being played back to it.</para>
108  </parameter>
109  <parameter name="Control" required="true">
110  <enumlist>
111  <enum name="stop">
112  <para>Stop the playback operation.</para>
113  </enum>
114  <enum name="forward">
115  <para>Move the current position in the media forward. The amount
116  of time that the stream moves forward is determined by the
117  <replaceable>skipms</replaceable> value passed to the application
118  that initiated the playback.</para>
119  <note>
120  <para>The default skipms value is <literal>3000</literal> ms.</para>
121  </note>
122  </enum>
123  <enum name="reverse">
124  <para>Move the current position in the media backward. The amount
125  of time that the stream moves backward is determined by the
126  <replaceable>skipms</replaceable> value passed to the application
127  that initiated the playback.</para>
128  <note>
129  <para>The default skipms value is <literal>3000</literal> ms.</para>
130  </note>
131  </enum>
132  <enum name="pause">
133  <para>Pause/unpause the playback operation, if supported.
134  If not supported, stop the playback.</para>
135  </enum>
136  <enum name="restart">
137  <para>Restart the playback operation, if supported.
138  If not supported, stop the playback.</para>
139  </enum>
140  </enumlist>
141  </parameter>
142  </syntax>
143  <description>
144  <para>Control the operation of a media file being played back to a channel.
145  Note that this AMI action does not initiate playback of media to channel, but
146  rather controls the operation of a media operation that was already initiated
147  on the channel.</para>
148  <note>
149  <para>The <literal>pause</literal> and <literal>restart</literal>
150  <replaceable>Control</replaceable> options will stop a playback
151  operation if that operation was not initiated from the
152  <replaceable>ControlPlayback</replaceable> application or the
153  <replaceable>control stream file</replaceable> AGI command.</para>
154  </note>
155  </description>
156  <see-also>
157  <ref type="application">Playback</ref>
158  <ref type="application">ControlPlayback</ref>
159  <ref type="agi">stream file</ref>
160  <ref type="agi">control stream file</ref>
161  </see-also>
162  </manager>
163  ***/
164 static const char app[] = "ControlPlayback";
165 
166 enum {
167  OPT_OFFSET = (1 << 1),
168 };
169 
170 enum {
172  /* must stay as the last entry ... */
174 };
175 
179 );
180 
181 static int is_on_phonepad(char key)
182 {
183  return key == 35 || key == 42 || (key >= 48 && key <= 57);
184 }
185 
186 static int is_argument(const char *haystack, int needle)
187 {
188  if (ast_strlen_zero(haystack))
189  return 0;
190 
191  if (strchr(haystack, needle))
192  return -1;
193 
194  return 0;
195 }
196 
197 static int controlplayback_exec(struct ast_channel *chan, const char *data)
198 {
199  int res = 0;
200  int skipms = 0;
201  long offsetms = 0;
202  char offsetbuf[20];
203  char stopkeybuf[2];
204  char *tmp;
205  struct ast_flags opts = { 0, };
206  char *opt_args[OPT_ARG_ARRAY_LEN];
208  AST_APP_ARG(filename);
209  AST_APP_ARG(skip);
210  AST_APP_ARG(fwd);
211  AST_APP_ARG(rev);
212  AST_APP_ARG(stop);
213  AST_APP_ARG(pause);
214  AST_APP_ARG(restart);
216  );
217 
218  if (ast_strlen_zero(data)) {
219  ast_log(LOG_WARNING, "ControlPlayback requires an argument (filename)\n");
220  return -1;
221  }
222 
223  tmp = ast_strdupa(data);
225 
226  if (args.argc < 1) {
227  ast_log(LOG_WARNING, "ControlPlayback requires an argument (filename)\n");
228  return -1;
229  }
230 
231  skipms = args.skip ? (atoi(args.skip) ? atoi(args.skip) : 3000) : 3000;
232 
233  if (!args.fwd || !is_on_phonepad(*args.fwd)) {
234  char *digit = "#";
235  if (!is_argument(args.rev, *digit) && !is_argument(args.stop, *digit) && !is_argument(args.pause, *digit) && !is_argument(args.restart, *digit))
236  args.fwd = digit;
237  else
238  args.fwd = NULL;
239  }
240  if (!args.rev || !is_on_phonepad(*args.rev)) {
241  char *digit = "*";
242  if (!is_argument(args.fwd, *digit) && !is_argument(args.stop, *digit) && !is_argument(args.pause, *digit) && !is_argument(args.restart, *digit))
243  args.rev = digit;
244  else
245  args.rev = NULL;
246  }
247  ast_debug(1, "Forward key = %s, Rewind key = %s\n", args.fwd, args.rev);
248  if (args.stop && !is_on_phonepad(*args.stop))
249  args.stop = NULL;
250  if (args.pause && !is_on_phonepad(*args.pause))
251  args.pause = NULL;
252  if (args.restart && !is_on_phonepad(*args.restart))
253  args.restart = NULL;
254 
255  if (args.options) {
256  ast_app_parse_options(cpb_opts, &opts, opt_args, args.options);
257  if (ast_test_flag(&opts, OPT_OFFSET))
258  offsetms = atol(opt_args[OPT_ARG_OFFSET]);
259  }
260 
261  res = ast_control_streamfile(chan, args.filename, args.fwd, args.rev, args.stop, args.pause, args.restart, skipms, &offsetms);
262 
263  /* If we stopped on one of our stop keys, return 0 */
264  if (res > 0 && args.stop && strchr(args.stop, res)) {
265  pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "USERSTOPPED");
266  snprintf(stopkeybuf, sizeof(stopkeybuf), "%c", res);
267  pbx_builtin_setvar_helper(chan, "CPLAYBACKSTOPKEY", stopkeybuf);
268  res = 0;
269  } else if (res > 0 && res == AST_CONTROL_STREAM_STOP) {
270  pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "REMOTESTOPPED");
271  res = 0;
272  } else {
273  if (res < 0) {
274  res = 0;
275  pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "ERROR");
276  } else
277  pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "SUCCESS");
278  }
279 
280  snprintf(offsetbuf, sizeof(offsetbuf), "%ld", offsetms);
281  pbx_builtin_setvar_helper(chan, "CPLAYBACKOFFSET", offsetbuf);
282 
283  return res;
284 }
285 
286 static int controlplayback_manager(struct mansession *s, const struct message *m)
287 {
288  const char *channel_name = astman_get_header(m, "Channel");
289  const char *control_type = astman_get_header(m, "Control");
290  struct ast_channel *chan;
291 
292  if (ast_strlen_zero(channel_name)) {
293  astman_send_error(s, m, "Channel not specified");
294  return 0;
295  }
296 
297  if (ast_strlen_zero(control_type)) {
298  astman_send_error(s, m, "Control not specified");
299  return 0;
300  }
301 
302  chan = ast_channel_get_by_name(channel_name);
303  if (!chan) {
304  astman_send_error(s, m, "No such channel");
305  return 0;
306  }
307 
308  if (!strcasecmp(control_type, "stop")) {
310  } else if (!strcasecmp(control_type, "forward")) {
312  } else if (!strcasecmp(control_type, "reverse")) {
314  } else if (!strcasecmp(control_type, "pause")) {
316  } else if (!strcasecmp(control_type, "restart")) {
318  } else {
319  astman_send_error(s, m, "Unknown control type");
320  chan = ast_channel_unref(chan);
321  return 0;
322  }
323 
324  chan = ast_channel_unref(chan);
325  astman_send_ack(s, m, NULL);
326  return 0;
327 }
328 
329 static int unload_module(void)
330 {
331  int res = 0;
332 
334  res |= ast_manager_unregister("ControlPlayback");
335 
336  return res;
337 }
338 
339 static int load_module(void)
340 {
341  int res = 0;
342 
345 
346  return res;
347 }
348 
349 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Control Playback Application");
char digit
Main Channel structure associated with a channel.
#define AST_MODULE_INFO_STANDARD(keystr, desc)
Definition: module.h:567
Asterisk main include file. File version handling, generic pbx functions.
int ast_queue_control(struct ast_channel *chan, enum ast_control_frame_type control)
Queue a control frame without payload.
Definition: channel.c:1231
struct ast_channel * chan
Definition: confbridge.h:277
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:2981
#define ast_test_flag(p, flag)
Definition: utils.h:63
#define BEGIN_OPTIONS
static int controlplayback_manager(struct mansession *s, const struct message *m)
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
#define LOG_WARNING
Definition: logger.h:274
static int tmp()
Definition: bt_open.c:389
#define EVENT_FLAG_CALL
Definition: manager.h:72
unsigned int stop
Definition: app_meetme.c:1096
static int load_module(void)
void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
Send ack in manager transaction.
Definition: manager.c:3191
const char * args
#define NULL
Definition: resample.c:96
int ast_control_streamfile(struct ast_channel *chan, const char *file, const char *fwd, const char *rev, const char *stop, const char *pause, const char *restart, int skipms, long *offsetms)
Stream a file with fast forward, pause, reverse, restart.
Definition: main/app.c:1319
static int is_argument(const char *haystack, int needle)
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
Utility functions.
const char * astman_get_header(const struct message *m, char *var)
Get header from mananger transaction.
Definition: manager.c:2820
#define ast_strlen_zero(foo)
Definition: strings.h:52
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
In case you didn&#39;t read that giant block of text above the mansession_session struct, the struct mansession is named this solely to keep the API the same in Asterisk. This structure really represents data that is different from Manager action to Manager action. The mansession_session pointer contained within points to session-specific data.
Definition: manager.c:1625
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
static int is_on_phonepad(char key)
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition: main/app.c:2906
Core PBX routines and definitions.
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
Definition: manager.c:7258
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
static int skipms
#define AST_APP_OPTION_ARG(option, flagno, argno)
Declares an application option that accepts an argument.
Structure used to handle boolean flags.
Definition: utils.h:199
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
static int unload_module(void)
#define END_OPTIONS
static const struct ast_app_option cpb_opts[128]
static const char app[]
static struct test_options options
#define ast_manager_register_xml(action, authority, func)
Register a manager callback using XML documentation to describe the manager.
Definition: manager.h:186
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
struct ast_channel * ast_channel_get_by_name(const char *name)
Find a channel by name.
Definition: channel.c:1454
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Definition: manager.c:3159
Asterisk module definitions.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
static int controlplayback_exec(struct ast_channel *chan, const char *data)
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:626
#define AST_APP_ARG(name)
Define an application argument.