Skip to content

Commit 973dc83

Browse files
committed
app_partialplayback: Add PartialPlayback app.
Adds an application that can play part of an audio file, rather than the entire file.
1 parent 33353b7 commit 973dc83

File tree

3 files changed

+221
-0
lines changed

3 files changed

+221
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ PhreakScript installs:
2323
- ``ReturnIf``
2424
- ``SendMail``
2525
- ``MallocTrim``
26+
- ``PartialPlayback``
2627
- ``LoopPlayback``
2728
- ``RandomPlayback``
2829
- ``SayTelephoneNumber``

apps/app_partialplayback.c

+219
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
/*
2+
* Asterisk -- An open source telephony toolkit.
3+
*
4+
* Copyright (C) 2023, Naveen Albert
5+
*
6+
* Naveen Albert <[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 Application to partially play a sound file
22+
*
23+
* \author Naveen Albert <[email protected]>
24+
*
25+
* \ingroup applications
26+
*/
27+
28+
/*** MODULEINFO
29+
<support_level>extended</support_level>
30+
***/
31+
32+
#include "asterisk.h"
33+
34+
#include "asterisk/file.h"
35+
#include "asterisk/pbx.h"
36+
#include "asterisk/module.h"
37+
#include "asterisk/app.h"
38+
#include "asterisk/mod_format.h" /* expose ast_filestream */
39+
40+
/*** DOCUMENTATION
41+
<application name="PartialPlayback" language="en_US">
42+
<synopsis>
43+
Play a file, between optionally specified start and end offsets.
44+
</synopsis>
45+
<syntax>
46+
<parameter name="filename" required="true">
47+
<para>File to play. Do not include extension.</para>
48+
</parameter>
49+
<parameter name="start">
50+
<para>Starting time. Default is start of file.</para>
51+
</parameter>
52+
<parameter name="end">
53+
<para>Ending time. Default is end of file.</para>
54+
</parameter>
55+
</syntax>
56+
<description>
57+
<para>Plays back a given filename (do not include extension).</para>
58+
<para>This application does not automatically answer the channel.</para>
59+
<para>This application sets the following channel variable upon completion:</para>
60+
<variablelist>
61+
<variable name="PLAYBACKSTATUS">
62+
<para>The status of the playback attempt.</para>
63+
<value name="SUCCESS"/>
64+
<value name="FAILED"/>
65+
</variable>
66+
</variablelist>
67+
</description>
68+
<see-also>
69+
<ref type="application">Playback</ref>
70+
<ref type="application">ControlPlayback</ref>
71+
<ref type="agi">stream file</ref>
72+
<ref type="agi">control stream file</ref>
73+
<ref type="manager">ControlPlayback</ref>
74+
</see-also>
75+
</application>
76+
***/
77+
78+
static char *app = "PartialPlayback";
79+
80+
static int playback_exec(struct ast_channel *chan, const char *data)
81+
{
82+
int res = 0;
83+
int start = 0, end = 0;
84+
char *tmp;
85+
const char *orig_chan_name = NULL;
86+
87+
AST_DECLARE_APP_ARGS(args,
88+
AST_APP_ARG(filename);
89+
AST_APP_ARG(start);
90+
AST_APP_ARG(end);
91+
);
92+
93+
if (ast_strlen_zero(data)) {
94+
ast_log(LOG_WARNING, "%s requires an argument (filename)\n", app);
95+
return -1;
96+
}
97+
98+
tmp = ast_strdupa(data);
99+
AST_STANDARD_APP_ARGS(args, tmp);
100+
101+
if (ast_strlen_zero(args.filename)) {
102+
ast_log(LOG_ERROR, "%s requires a filename\n", app);
103+
return -1;
104+
}
105+
106+
if (!ast_strlen_zero(args.start)) {
107+
start = atoi(args.start);
108+
if (start < 0) {
109+
ast_log(LOG_ERROR, "Invalid start time: %s\n", args.start);
110+
return -1;
111+
}
112+
}
113+
if (!ast_strlen_zero(args.end)) {
114+
end = atoi(args.end);
115+
}
116+
117+
if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_MASQ_NOSTREAM)) {
118+
orig_chan_name = ast_strdupa(ast_channel_name(chan));
119+
}
120+
121+
ast_stopstream(chan);
122+
res = ast_streamfile(chan, args.filename, ast_channel_language(chan));
123+
if (res) {
124+
goto end;
125+
}
126+
127+
if (start) {
128+
/* Seek to starting position - based on waitstream_control in file.c */
129+
int eoftest;
130+
/* We're fast forwarding from the beginning, so this is a convenient wrapper
131+
* to seek to the right spot, using ms instead of samples. */
132+
ast_stream_fastforward(ast_channel_stream(chan), start);
133+
eoftest = fgetc(ast_channel_stream(chan)->f);
134+
if (feof(ast_channel_stream(chan)->f)) {
135+
ast_stream_rewind(ast_channel_stream(chan), start);
136+
} else {
137+
ungetc(eoftest, ast_channel_stream(chan)->f);
138+
}
139+
}
140+
141+
if (end) {
142+
/* Play until we get to end time */
143+
while (ast_channel_stream(chan)) {
144+
/* Based on waitstream_core in file.c */
145+
int res;
146+
int ms;
147+
struct ast_frame *fr;
148+
off_t offset;
149+
int ms_len;
150+
151+
if (orig_chan_name && strcasecmp(orig_chan_name, ast_channel_name(chan))) {
152+
ast_stopstream(chan);
153+
res = -1;
154+
break;
155+
}
156+
ms = ast_sched_wait(ast_channel_sched(chan));
157+
if (ms < 0 && !ast_channel_timingfunc(chan)) {
158+
ast_stopstream(chan);
159+
break;
160+
}
161+
if (ms < 0) {
162+
ms = 1000;
163+
}
164+
res = ast_waitfor(chan, ms);
165+
if (res < 0) {
166+
ast_log(LOG_WARNING, "ast_waitfor failed (%s)\n", strerror(errno));
167+
break;
168+
}
169+
fr = ast_read(chan);
170+
if (!fr) {
171+
ast_debug(3, "Channel %s did not return a frame, must've hung up\n", ast_channel_name(chan));
172+
break;
173+
}
174+
switch (fr->frametype) {
175+
case AST_FRAME_CONTROL:
176+
switch (fr->subclass.integer) {
177+
case AST_CONTROL_HANGUP:
178+
goto end;
179+
default:
180+
break;
181+
}
182+
default:
183+
break;
184+
}
185+
/* Is our time yet come? */
186+
offset = ast_tellstream(ast_channel_stream(chan));
187+
ms_len = offset / (ast_format_get_sample_rate(ast_channel_stream(chan)->fmt->format) / 1000);
188+
if (ms_len >= end) {
189+
ast_debug(1, "Stopping stream because we reached %d ms\n", end);
190+
break;
191+
}
192+
ast_sched_runq(ast_channel_sched(chan));
193+
}
194+
} else {
195+
/* Play the remainder */
196+
res = ast_waitstream(chan, "");
197+
}
198+
ast_stopstream(chan);
199+
200+
end:
201+
if (res && !ast_check_hangup(chan)) {
202+
ast_log(LOG_WARNING, "%s failed on %s for %s\n", app, ast_channel_name(chan), args.filename);
203+
}
204+
205+
pbx_builtin_setvar_helper(chan, "PLAYBACKSTATUS", res ? "FAILED" : "SUCCESS");
206+
return res;
207+
}
208+
209+
static int unload_module(void)
210+
{
211+
return ast_unregister_application(app);
212+
}
213+
214+
static int load_module(void)
215+
{
216+
return ast_register_application_xml(app, playback_exec);
217+
}
218+
219+
AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Partial playback application");

phreaknet.sh

+1
Original file line numberDiff line numberDiff line change
@@ -1452,6 +1452,7 @@ phreak_patches() { # $1 = $PATCH_DIR, $2 = $AST_SRC_DIR
14521452
phreak_tree_module "apps/app_mail.c"
14531453
phreak_tree_module "apps/app_memory.c"
14541454
phreak_tree_module "apps/app_mwi.c"
1455+
phreak_tree_module "apps/app_partialplayback.c"
14551456
phreak_tree_module "apps/app_playdigits.c"
14561457
phreak_tree_module "apps/app_predial.c"
14571458
phreak_tree_module "apps/app_pulsar.c"

0 commit comments

Comments
 (0)