app_controlplayback.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 1999 - 2005, Digium, Inc.
  5. *
  6. * Mark Spencer <markster@digium.com>
  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. /*! \file
  19. *
  20. * \brief Trivial application to control playback of a sound file
  21. *
  22. * \author Mark Spencer <markster@digium.com>
  23. *
  24. * \ingroup applications
  25. */
  26. /*** MODULEINFO
  27. <support_level>core</support_level>
  28. ***/
  29. #include "asterisk.h"
  30. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  31. #include "asterisk/pbx.h"
  32. #include "asterisk/app.h"
  33. #include "asterisk/module.h"
  34. #include "asterisk/manager.h"
  35. #include "asterisk/utils.h"
  36. #include "asterisk/astobj2.h"
  37. /*** DOCUMENTATION
  38. <application name="ControlPlayback" language="en_US">
  39. <synopsis>
  40. Play a file with fast forward and rewind.
  41. </synopsis>
  42. <syntax>
  43. <parameter name="filename" required="true" />
  44. <parameter name="skipms">
  45. <para>This is number of milliseconds to skip when rewinding or
  46. fast-forwarding.</para>
  47. </parameter>
  48. <parameter name="ff">
  49. <para>Fast-forward when this DTMF digit is received. (defaults to <literal>#</literal>)</para>
  50. </parameter>
  51. <parameter name="rew">
  52. <para>Rewind when this DTMF digit is received. (defaults to <literal>*</literal>)</para>
  53. </parameter>
  54. <parameter name="stop">
  55. <para>Stop playback when this DTMF digit is received.</para>
  56. </parameter>
  57. <parameter name="pause">
  58. <para>Pause playback when this DTMF digit is received.</para>
  59. </parameter>
  60. <parameter name="restart">
  61. <para>Restart playback when this DTMF digit is received.</para>
  62. </parameter>
  63. <parameter name="options">
  64. <optionlist>
  65. <option name="o">
  66. <argument name="time" required="true">
  67. <para>Start at <replaceable>time</replaceable> ms from the
  68. beginning of the file.</para>
  69. </argument>
  70. </option>
  71. </optionlist>
  72. </parameter>
  73. </syntax>
  74. <description>
  75. <para>This application will play back the given <replaceable>filename</replaceable>.</para>
  76. <para>It sets the following channel variables upon completion:</para>
  77. <variablelist>
  78. <variable name="CPLAYBACKSTATUS">
  79. <para>Contains the status of the attempt as a text string</para>
  80. <value name="SUCCESS" />
  81. <value name="USERSTOPPED" />
  82. <value name="REMOTESTOPPED" />
  83. <value name="ERROR" />
  84. </variable>
  85. <variable name="CPLAYBACKOFFSET">
  86. <para>Contains the offset in ms into the file where playback
  87. was at when it stopped. <literal>-1</literal> is end of file.</para>
  88. </variable>
  89. <variable name="CPLAYBACKSTOPKEY">
  90. <para>If the playback is stopped by the user this variable contains
  91. the key that was pressed.</para>
  92. </variable>
  93. </variablelist>
  94. </description>
  95. </application>
  96. <manager name="ControlPlayback" language="en_US">
  97. <synopsis>
  98. Control the playback of a file being played to a channel.
  99. </synopsis>
  100. <syntax>
  101. <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
  102. <parameter name="Channel" required="true">
  103. <para>The name of the channel that currently has a file being played back to it.</para>
  104. </parameter>
  105. <parameter name="Control" required="true">
  106. <enumlist>
  107. <enum name="stop">
  108. <para>Stop the playback operation.</para>
  109. </enum>
  110. <enum name="forward">
  111. <para>Move the current position in the media forward. The amount
  112. of time that the stream moves forward is determined by the
  113. <replaceable>skipms</replaceable> value passed to the application
  114. that initiated the playback.</para>
  115. <note>
  116. <para>The default skipms value is <literal>3000</literal> ms.</para>
  117. </note>
  118. </enum>
  119. <enum name="reverse">
  120. <para>Move the current position in the media backward. The amount
  121. of time that the stream moves backward is determined by the
  122. <replaceable>skipms</replaceable> value passed to the application
  123. that initiated the playback.</para>
  124. <note>
  125. <para>The default skipms value is <literal>3000</literal> ms.</para>
  126. </note>
  127. </enum>
  128. <enum name="pause">
  129. <para>Pause/unpause the playback operation, if supported.
  130. If not supported, stop the playback.</para>
  131. </enum>
  132. <enum name="restart">
  133. <para>Restart the playback operation, if supported.
  134. If not supported, stop the playback.</para>
  135. </enum>
  136. </enumlist>
  137. </parameter>
  138. </syntax>
  139. <description>
  140. <para>Control the operation of a media file being played back to a channel.
  141. Note that this AMI action does not initiate playback of media to channel, but
  142. rather controls the operation of a media operation that was already initiated
  143. on the channel.</para>
  144. <note>
  145. <para>The <literal>pause</literal> and <literal>restart</literal>
  146. <replaceable>Control</replaceable> options will stop a playback
  147. operation if that operation was not initiated from the
  148. <replaceable>ControlPlayback</replaceable> application or the
  149. <replaceable>control stream file</replaceable> AGI command.</para>
  150. </note>
  151. </description>
  152. <see-also>
  153. <ref type="application">Playback</ref>
  154. <ref type="application">ControlPlayback</ref>
  155. <ref type="agi">stream file</ref>
  156. <ref type="agi">control stream file</ref>
  157. </see-also>
  158. </manager>
  159. ***/
  160. static const char app[] = "ControlPlayback";
  161. enum {
  162. OPT_OFFSET = (1 << 1),
  163. };
  164. enum {
  165. OPT_ARG_OFFSET = 0,
  166. /* must stay as the last entry ... */
  167. OPT_ARG_ARRAY_LEN,
  168. };
  169. AST_APP_OPTIONS(cpb_opts, BEGIN_OPTIONS
  170. AST_APP_OPTION_ARG('o', OPT_OFFSET, OPT_ARG_OFFSET),
  171. END_OPTIONS
  172. );
  173. static int is_on_phonepad(char key)
  174. {
  175. return key == 35 || key == 42 || (key >= 48 && key <= 57);
  176. }
  177. static int is_argument(const char *haystack, int needle)
  178. {
  179. if (ast_strlen_zero(haystack))
  180. return 0;
  181. if (strchr(haystack, needle))
  182. return -1;
  183. return 0;
  184. }
  185. static int controlplayback_exec(struct ast_channel *chan, const char *data)
  186. {
  187. int res = 0;
  188. int skipms = 0;
  189. long offsetms = 0;
  190. char offsetbuf[20];
  191. char stopkeybuf[2];
  192. char *tmp;
  193. struct ast_flags opts = { 0, };
  194. char *opt_args[OPT_ARG_ARRAY_LEN];
  195. AST_DECLARE_APP_ARGS(args,
  196. AST_APP_ARG(filename);
  197. AST_APP_ARG(skip);
  198. AST_APP_ARG(fwd);
  199. AST_APP_ARG(rev);
  200. AST_APP_ARG(stop);
  201. AST_APP_ARG(pause);
  202. AST_APP_ARG(restart);
  203. AST_APP_ARG(options);
  204. );
  205. if (ast_strlen_zero(data)) {
  206. ast_log(LOG_WARNING, "ControlPlayback requires an argument (filename)\n");
  207. return -1;
  208. }
  209. tmp = ast_strdupa(data);
  210. AST_STANDARD_APP_ARGS(args, tmp);
  211. if (args.argc < 1) {
  212. ast_log(LOG_WARNING, "ControlPlayback requires an argument (filename)\n");
  213. return -1;
  214. }
  215. skipms = args.skip ? (atoi(args.skip) ? atoi(args.skip) : 3000) : 3000;
  216. if (!args.fwd || !is_on_phonepad(*args.fwd)) {
  217. char *digit = "#";
  218. if (!is_argument(args.rev, *digit) && !is_argument(args.stop, *digit) && !is_argument(args.pause, *digit) && !is_argument(args.restart, *digit))
  219. args.fwd = digit;
  220. else
  221. args.fwd = NULL;
  222. }
  223. if (!args.rev || !is_on_phonepad(*args.rev)) {
  224. char *digit = "*";
  225. if (!is_argument(args.fwd, *digit) && !is_argument(args.stop, *digit) && !is_argument(args.pause, *digit) && !is_argument(args.restart, *digit))
  226. args.rev = digit;
  227. else
  228. args.rev = NULL;
  229. }
  230. ast_debug(1, "Forward key = %s, Rewind key = %s\n", args.fwd, args.rev);
  231. if (args.stop && !is_on_phonepad(*args.stop))
  232. args.stop = NULL;
  233. if (args.pause && !is_on_phonepad(*args.pause))
  234. args.pause = NULL;
  235. if (args.restart && !is_on_phonepad(*args.restart))
  236. args.restart = NULL;
  237. if (args.options) {
  238. ast_app_parse_options(cpb_opts, &opts, opt_args, args.options);
  239. if (ast_test_flag(&opts, OPT_OFFSET))
  240. offsetms = atol(opt_args[OPT_ARG_OFFSET]);
  241. }
  242. res = ast_control_streamfile(chan, args.filename, args.fwd, args.rev, args.stop, args.pause, args.restart, skipms, &offsetms);
  243. /* If we stopped on one of our stop keys, return 0 */
  244. if (res > 0 && args.stop && strchr(args.stop, res)) {
  245. pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "USERSTOPPED");
  246. snprintf(stopkeybuf, sizeof(stopkeybuf), "%c", res);
  247. pbx_builtin_setvar_helper(chan, "CPLAYBACKSTOPKEY", stopkeybuf);
  248. res = 0;
  249. } else if (res > 0 && res == AST_CONTROL_STREAM_STOP) {
  250. pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "REMOTESTOPPED");
  251. res = 0;
  252. } else {
  253. if (res < 0) {
  254. res = 0;
  255. pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "ERROR");
  256. } else
  257. pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "SUCCESS");
  258. }
  259. snprintf(offsetbuf, sizeof(offsetbuf), "%ld", offsetms);
  260. pbx_builtin_setvar_helper(chan, "CPLAYBACKOFFSET", offsetbuf);
  261. return res;
  262. }
  263. static int controlplayback_manager(struct mansession *s, const struct message *m)
  264. {
  265. const char *channel_name = astman_get_header(m, "Channel");
  266. const char *control_type = astman_get_header(m, "Control");
  267. struct ast_channel *chan;
  268. if (ast_strlen_zero(channel_name)) {
  269. astman_send_error(s, m, "Channel not specified");
  270. return 0;
  271. }
  272. if (ast_strlen_zero(control_type)) {
  273. astman_send_error(s, m, "Control not specified");
  274. return 0;
  275. }
  276. chan = ast_channel_get_by_name(channel_name);
  277. if (!chan) {
  278. astman_send_error(s, m, "No such channel");
  279. return 0;
  280. }
  281. if (!strcasecmp(control_type, "stop")) {
  282. ast_queue_control(chan, AST_CONTROL_STREAM_STOP);
  283. } else if (!strcasecmp(control_type, "forward")) {
  284. ast_queue_control(chan, AST_CONTROL_STREAM_FORWARD);
  285. } else if (!strcasecmp(control_type, "reverse")) {
  286. ast_queue_control(chan, AST_CONTROL_STREAM_REVERSE);
  287. } else if (!strcasecmp(control_type, "pause")) {
  288. ast_queue_control(chan, AST_CONTROL_STREAM_SUSPEND);
  289. } else if (!strcasecmp(control_type, "restart")) {
  290. ast_queue_control(chan, AST_CONTROL_STREAM_RESTART);
  291. } else {
  292. astman_send_error(s, m, "Unknown control type");
  293. chan = ast_channel_unref(chan);
  294. return 0;
  295. }
  296. chan = ast_channel_unref(chan);
  297. astman_send_ack(s, m, NULL);
  298. return 0;
  299. }
  300. static int unload_module(void)
  301. {
  302. int res = 0;
  303. res |= ast_unregister_application(app);
  304. res |= ast_manager_unregister("ControlPlayback");
  305. return res;
  306. }
  307. static int load_module(void)
  308. {
  309. int res = 0;
  310. res |= ast_register_application_xml(app, controlplayback_exec);
  311. res |= ast_manager_register_xml("ControlPlayback", EVENT_FLAG_CALL, controlplayback_manager);
  312. return res;
  313. }
  314. AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Control Playback Application");