Skip to content

Commit 40c4d48

Browse files
committed
app_ffplayer: Add ffmpeg audio player application.
Add an application similar to MP3Player, but for ffmpeg instead of mpg123.
1 parent 7e6bcb8 commit 40c4d48

File tree

3 files changed

+278
-0
lines changed

3 files changed

+278
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ PhreakScript installs:
6060
- ``PreDial``
6161
- ``SetMWI``
6262
- ``PlayDigits``
63+
- ``FFPlayer``
6364
- ``StreamSilence``
6465
- ``RevertivePulse``
6566
- ``OnHook``

apps/app_ffplayer.c

Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
/*
2+
* Asterisk -- An open source telephony toolkit.
3+
*
4+
* Copyright (C) 2025, 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 play audio streams using ffmpeg
22+
*
23+
* \author Naveen Albert <[email protected]>
24+
*
25+
* \note Based off app_mp3, by Mark Spencer <[email protected]>
26+
*
27+
* \ingroup applications
28+
*/
29+
30+
/*** MODULEINFO
31+
<support_level>extended</support_level>
32+
***/
33+
34+
#include "asterisk.h"
35+
36+
#include <sys/time.h>
37+
#include <sys/types.h>
38+
#include <signal.h>
39+
40+
#include "asterisk/lock.h"
41+
#include "asterisk/file.h"
42+
#include "asterisk/channel.h"
43+
#include "asterisk/frame.h"
44+
#include "asterisk/pbx.h"
45+
#include "asterisk/module.h"
46+
#include "asterisk/translate.h"
47+
#include "asterisk/app.h"
48+
#include "asterisk/format_cache.h"
49+
50+
#define FFMPEG "/usr/bin/ffmpeg"
51+
52+
/*** DOCUMENTATION
53+
<application name="FFPlayer" language="en_US">
54+
<synopsis>
55+
Play a file or stream using ffmpeg.
56+
</synopsis>
57+
<syntax>
58+
<parameter name="Location" required="true">
59+
<para>Location of the file to be played.
60+
(argument passed to ffmpeg)</para>
61+
</parameter>
62+
</syntax>
63+
<description>
64+
<para>Executes ffmpeg to play the given location.</para>
65+
<para>User can exit by pressing any key on the dialpad, or by hanging up.</para>
66+
<example title="Play an FF playlist">
67+
exten => 1234,1,FFPlayer(/var/lib/asterisk/playlist.m3u)
68+
</example>
69+
<para>This application does not automatically answer and should be preceded by an
70+
application such as Answer() or Progress().</para>
71+
</description>
72+
</application>
73+
74+
***/
75+
static char *app = "FFPlayer";
76+
77+
static int ffplay(const char *filename, unsigned int sampling_rate, int fd)
78+
{
79+
int res;
80+
char sampling_rate_str[8];
81+
82+
res = ast_safe_fork(0);
83+
if (res < 0)
84+
ast_log(LOG_WARNING, "Fork failed\n");
85+
if (res) {
86+
return res;
87+
}
88+
if (ast_opt_high_priority)
89+
ast_set_priority(0);
90+
91+
dup2(fd, STDOUT_FILENO);
92+
ast_close_fds_above_n(STDERR_FILENO);
93+
94+
snprintf(sampling_rate_str, 8, "%u", sampling_rate);
95+
96+
execl(FFMPEG, "ffmpeg", "-i", filename, "-ar", sampling_rate_str, "-ac", "1", "-acodec", "pcm_s16le", "-f", "s16le", "-bufsize", "64k", "pipe:1", (char *) NULL);
97+
98+
/* Can't use ast_log since FD's are closed */
99+
fprintf(stderr, "Execute of ffmpeg failed\n");
100+
_exit(0);
101+
}
102+
103+
static int timed_read(int fd, void *data, int datalen, int timeout, int pid)
104+
{
105+
int res;
106+
int i;
107+
struct pollfd fds[1];
108+
fds[0].fd = fd;
109+
fds[0].events = POLLIN;
110+
for (i = 0; i < timeout; i++) {
111+
res = ast_poll(fds, 1, 1000);
112+
if (res > 0) {
113+
break;
114+
} else if (res == 0) {
115+
ast_log(LOG_WARNING, "No activity returned from stream\n");
116+
/* is ffmpeg still running? */
117+
kill(pid, 0);
118+
if (errno == ESRCH) {
119+
return -1;
120+
}
121+
} else {
122+
ast_log(LOG_NOTICE, "error polling ffmpeg: %s\n", strerror(errno));
123+
return -1;
124+
}
125+
}
126+
127+
if (i == timeout) {
128+
ast_log(LOG_NOTICE, "Poll timed out.\n");
129+
return -1;
130+
}
131+
132+
return read(fd, data, datalen);
133+
134+
}
135+
136+
static int ff_exec(struct ast_channel *chan, const char *data)
137+
{
138+
int res=0;
139+
int fds[2];
140+
int ms = -1;
141+
int pid = -1;
142+
RAII_VAR(struct ast_format *, owriteformat, NULL, ao2_cleanup);
143+
int timeout = 2;
144+
int startedff = 0;
145+
struct timeval next;
146+
struct ast_frame *f;
147+
struct myframe {
148+
struct ast_frame f;
149+
char offset[AST_FRIENDLY_OFFSET];
150+
short frdata[160];
151+
} myf = {
152+
.f = { 0, },
153+
};
154+
struct ast_format * native_format;
155+
unsigned int sampling_rate;
156+
struct ast_format * write_format;
157+
158+
if (ast_strlen_zero(data)) {
159+
ast_log(LOG_WARNING, "FFPlayer requires an argument (filename)\n");
160+
return -1;
161+
}
162+
163+
if (pipe(fds)) {
164+
ast_log(LOG_WARNING, "Unable to create pipe\n");
165+
return -1;
166+
}
167+
168+
ast_stopstream(chan);
169+
170+
native_format = ast_format_cap_get_format(ast_channel_nativeformats(chan), 0);
171+
sampling_rate = ast_format_get_sample_rate(native_format);
172+
write_format = ast_format_cache_get_slin_by_rate(sampling_rate);
173+
174+
owriteformat = ao2_bump(ast_channel_writeformat(chan));
175+
res = ast_set_write_format(chan, write_format);
176+
if (res < 0) {
177+
ast_log(LOG_WARNING, "Unable to set write format to signed linear\n");
178+
return -1;
179+
}
180+
181+
myf.f.frametype = AST_FRAME_VOICE;
182+
myf.f.subclass.format = write_format;
183+
myf.f.mallocd = 0;
184+
myf.f.offset = AST_FRIENDLY_OFFSET;
185+
myf.f.src = __PRETTY_FUNCTION__;
186+
myf.f.delivery.tv_sec = 0;
187+
myf.f.delivery.tv_usec = 0;
188+
myf.f.data.ptr = myf.frdata;
189+
190+
res = ffplay(data, sampling_rate, fds[1]);
191+
if (!strncasecmp(data, "http://", 7)) {
192+
timeout = 10;
193+
}
194+
/* Wait 1000 ms first */
195+
next = ast_tvnow();
196+
next.tv_sec += 1;
197+
if (res >= 0) {
198+
pid = res;
199+
/* Order is important -- there's almost always going to be ff... we want to prioritize the
200+
user */
201+
for (;;) {
202+
ms = ast_tvdiff_ms(next, ast_tvnow());
203+
if (ms <= 0) {
204+
res = timed_read(fds[0], myf.frdata, sizeof(myf.frdata), timeout, pid);
205+
if (res > 0) {
206+
myf.f.datalen = res;
207+
myf.f.samples = res / 2;
208+
startedff = 1;
209+
if (ast_write(chan, &myf.f) < 0) {
210+
res = -1;
211+
break;
212+
}
213+
} else {
214+
ast_debug(1, "No more ffmpeg\n");
215+
if (!startedff) { /* we couldn't do anything, which means this stream doesn't work */
216+
if (!strncasecmp(data, "https://", 8)) {
217+
ast_log(LOG_WARNING, "%s() does not support HTTPS streams. Use HTTP instead.\n", app);
218+
}
219+
ast_log(LOG_WARNING, "ffmpeg stream '%s' is broken or nonexistent\n", data);
220+
}
221+
res = 0;
222+
break;
223+
}
224+
next = ast_tvadd(next, ast_samp2tv(myf.f.samples, sampling_rate));
225+
} else {
226+
ms = ast_waitfor(chan, ms);
227+
if (ms < 0) {
228+
ast_debug(1, "Hangup detected\n");
229+
res = -1;
230+
break;
231+
}
232+
if (ms) {
233+
f = ast_read(chan);
234+
if (!f) {
235+
ast_debug(1, "Null frame == hangup() detected\n");
236+
res = -1;
237+
break;
238+
}
239+
if (f->frametype == AST_FRAME_DTMF) {
240+
ast_debug(1, "User pressed a key\n");
241+
ast_frfree(f);
242+
res = 0;
243+
break;
244+
}
245+
ast_frfree(f);
246+
}
247+
}
248+
}
249+
}
250+
close(fds[0]);
251+
close(fds[1]);
252+
253+
if (pid > -1)
254+
kill(pid, SIGKILL);
255+
if (!res && owriteformat)
256+
ast_set_write_format(chan, owriteformat);
257+
258+
ast_frfree(&myf.f);
259+
260+
return res;
261+
}
262+
263+
static int unload_module(void)
264+
{
265+
return ast_unregister_application(app);
266+
}
267+
268+
static int load_module(void)
269+
{
270+
return ast_register_application_xml(app, ff_exec);
271+
}
272+
273+
AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "FFmpeg player application");

phreaknet.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1498,6 +1498,9 @@ github_waitfor_ratelimit_reset() {
14981498
remaining=$( grep "x-ratelimit-remaining: 0" $CURL_HEADERS_DUMPFILE )
14991499
if [ $? -ne 0 ]; then
15001500
grep "x-ratelimit-remaining: " $CURL_HEADERS_DUMPFILE
1501+
if [ $? -ne 0 ]; then
1502+
cat $CURL_HEADERS_DUMPFILE
1503+
fi
15011504
remaining=$( grep "x-ratelimit-remaining: " $CURL_HEADERS_DUMPFILE | cut -d':' -f2 | xargs | tr -d '\r' | tr -d '\n' )
15021505
printf "Download failed, but have $remaining rate limit remaining...\n"
15031506
return
@@ -2452,6 +2455,7 @@ phreak_patches() {
24522455
phreak_tree_module "apps/app_ccsa.c"
24532456
phreak_tree_module "apps/app_dahdimonitor.c"
24542457
phreak_tree_module "apps/app_dialtone.c"
2458+
phreak_tree_module "apps/app_ffplayer.c"
24552459
phreak_tree_module "apps/app_frame.c"
24562460
phreak_tree_module "apps/app_george.c"
24572461
phreak_tree_module "apps/app_hookstate.c"

0 commit comments

Comments
 (0)