forked from asterisk/asterisk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapp_audiosocket.c
240 lines (209 loc) · 6.63 KB
/
app_audiosocket.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2019, CyCore Systems, Inc
*
* Seán C McCord <[email protected]>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*! \file
*
* \brief AudioSocket application -- transmit and receive audio through a TCP socket
*
* \author Seán C McCord <[email protected]>
*
* \ingroup applications
*/
/*** MODULEINFO
<depend>res_audiosocket</depend>
<support_level>extended</support_level>
***/
#include "asterisk.h"
#include "errno.h"
#include <uuid/uuid.h>
#include "asterisk/file.h"
#include "asterisk/module.h"
#include "asterisk/channel.h"
#include "asterisk/app.h"
#include "asterisk/res_audiosocket.h"
#include "asterisk/utils.h"
#include "asterisk/format_cache.h"
#define AST_MODULE "app_audiosocket"
#define AUDIOSOCKET_CONFIG "audiosocket.conf"
#define MAX_CONNECT_TIMEOUT_MSEC 2000
/*** DOCUMENTATION
<application name="AudioSocket" language="en_US">
<synopsis>
Transmit and receive audio between channel and TCP socket
</synopsis>
<syntax>
<parameter name="uuid" required="true">
<para>UUID is the universally-unique identifier of the call for the audio socket service. This ID must conform to the string form of a standard UUID.</para>
</parameter>
<parameter name="service" required="true">
<para>Service is the name or IP address and port number of the audio socket service to which this call should be connected. This should be in the form host:port, such as myserver:9019 </para>
</parameter>
</syntax>
<description>
<para>Connects to the given TCP service, then transmits channel audio over that socket. In turn, audio is received from the socket and sent to the channel. Only audio frames will be transmitted.</para>
<para>Protocol is specified at https://wiki.asterisk.org/wiki/display/AST/AudioSocket</para>
<para>This application does not automatically answer and should generally be preceeded by an application such as Answer() or Progress().</para>
</description>
</application>
***/
static const char app[] = "AudioSocket";
static int audiosocket_run(struct ast_channel *chan, const char *id, const int svc);
static int audiosocket_exec(struct ast_channel *chan, const char *data)
{
char *parse;
struct ast_format *readFormat, *writeFormat;
const char *chanName;
int res;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(idStr);
AST_APP_ARG(server);
);
int s = 0;
uuid_t uu;
chanName = ast_channel_name(chan);
/* Parse and validate arguments */
parse = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, parse);
if (ast_strlen_zero(args.idStr)) {
ast_log(LOG_ERROR, "UUID is required\n");
return -1;
}
if (uuid_parse(args.idStr, uu)) {
ast_log(LOG_ERROR, "Failed to parse UUID '%s'\n", args.idStr);
return -1;
}
if ((s = ast_audiosocket_connect(args.server, chan)) < 0) {
/* The res module will already output a log message, so another is not needed */
return -1;
}
writeFormat = ao2_bump(ast_channel_writeformat(chan));
readFormat = ao2_bump(ast_channel_readformat(chan));
if (ast_set_write_format(chan, ast_format_slin)) {
ast_log(LOG_ERROR, "Failed to set write format to SLINEAR for channel %s\n", chanName);
ao2_ref(writeFormat, -1);
ao2_ref(readFormat, -1);
close(s);
return -1;
}
if (ast_set_read_format(chan, ast_format_slin)) {
ast_log(LOG_ERROR, "Failed to set read format to SLINEAR for channel %s\n", chanName);
/* Attempt to restore previous write format even though it is likely to
* fail, since setting the read format did.
*/
if (ast_set_write_format(chan, writeFormat)) {
ast_log(LOG_ERROR, "Failed to restore write format for channel %s\n", chanName);
}
ao2_ref(writeFormat, -1);
ao2_ref(readFormat, -1);
close(s);
return -1;
}
res = audiosocket_run(chan, args.idStr, s);
/* On non-zero return, report failure */
if (res) {
/* Restore previous formats and close the connection */
if (ast_set_write_format(chan, writeFormat)) {
ast_log(LOG_ERROR, "Failed to restore write format for channel %s\n", chanName);
}
if (ast_set_read_format(chan, readFormat)) {
ast_log(LOG_ERROR, "Failed to restore read format for channel %s\n", chanName);
}
ao2_ref(writeFormat, -1);
ao2_ref(readFormat, -1);
close(s);
return res;
}
close(s);
if (ast_set_write_format(chan, writeFormat)) {
ast_log(LOG_ERROR, "Failed to restore write format for channel %s\n", chanName);
}
if (ast_set_read_format(chan, readFormat)) {
ast_log(LOG_ERROR, "Failed to restore read format for channel %s\n", chanName);
}
ao2_ref(writeFormat, -1);
ao2_ref(readFormat, -1);
return 0;
}
static int audiosocket_run(struct ast_channel *chan, const char *id, int svc)
{
const char *chanName;
struct ast_channel *targetChan;
int ms = 0;
int outfd = -1;
struct ast_frame *f;
if (!chan || ast_channel_state(chan) != AST_STATE_UP) {
ast_log(LOG_ERROR, "Channel is %s\n", chan ? "not answered" : "missing");
return -1;
}
if (ast_audiosocket_init(svc, id)) {
ast_log(LOG_ERROR, "Failed to intialize AudioSocket\n");
return -1;
}
chanName = ast_channel_name(chan);
while (1) {
targetChan = ast_waitfor_nandfds(&chan, 1, &svc, 1, NULL, &outfd, &ms);
if (targetChan) {
f = ast_read(chan);
if (!f) {
return -1;
}
if (f->frametype == AST_FRAME_VOICE) {
/* Send audio frame to audiosocket */
if (ast_audiosocket_send_frame(svc, f)) {
ast_log(LOG_ERROR, "Failed to forward channel frame from %s to AudioSocket\n",
chanName);
ast_frfree(f);
return -1;
}
}
ast_frfree(f);
}
if (outfd >= 0) {
f = ast_audiosocket_receive_frame(svc);
if (!f) {
ast_log(LOG_ERROR, "Failed to receive frame from AudioSocket message for"
"channel %s\n", chanName);
return -1;
}
if (ast_write(chan, f)) {
ast_log(LOG_WARNING, "Failed to forward frame to channel %s\n", chanName);
ast_frfree(f);
return -1;
}
ast_frfree(f);
}
}
return 0;
}
static int unload_module(void)
{
return ast_unregister_application(app);
}
static int load_module(void)
{
return ast_register_application_xml(app, audiosocket_exec);
}
AST_MODULE_INFO(
ASTERISK_GPL_KEY,
AST_MODFLAG_LOAD_ORDER,
"AudioSocket Application",
.support_level = AST_MODULE_SUPPORT_EXTENDED,
.load = load_module,
.unload = unload_module,
.load_pri = AST_MODPRI_CHANNEL_DRIVER,
.requires = "res_audiosocket",
);