Asterisk - The Open Source Telephony Project GIT-master-a358458
app_audiosocket.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2019, CyCore Systems, Inc
5 *
6 * Seán C McCord <scm@cycoresys.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
19/*! \file
20 *
21 * \brief AudioSocket application -- transmit and receive audio through a TCP socket
22 *
23 * \author Seán C McCord <scm@cycoresys.com>
24 *
25 * \ingroup applications
26 */
27
28/*** MODULEINFO
29 <depend>res_audiosocket</depend>
30 <support_level>extended</support_level>
31 ***/
32
33#include "asterisk.h"
34#include "errno.h"
35#include <uuid/uuid.h>
36
37#include "asterisk/file.h"
38#include "asterisk/module.h"
39#include "asterisk/channel.h"
40#include "asterisk/app.h"
42#include "asterisk/utils.h"
44
45#define AST_MODULE "app_audiosocket"
46#define AUDIOSOCKET_CONFIG "audiosocket.conf"
47#define MAX_CONNECT_TIMEOUT_MSEC 2000
48
49/*** DOCUMENTATION
50 <application name="AudioSocket" language="en_US">
51 <synopsis>
52 Transmit and receive audio between channel and TCP socket
53 </synopsis>
54 <syntax>
55 <parameter name="uuid" required="true">
56 <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>
57 </parameter>
58 <parameter name="service" required="true">
59 <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>
60 </parameter>
61 </syntax>
62 <description>
63 <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>
64 <para>Protocol is specified at https://docs.asterisk.org/Configuration/Channel-Drivers/AudioSocket/</para>
65 <para>This application does not automatically answer and should generally be preceeded by an application such as Answer() or Progress().</para>
66 </description>
67 </application>
68 ***/
69
70static const char app[] = "AudioSocket";
71
72static int audiosocket_run(struct ast_channel *chan, const char *id, const int svc);
73
74static int audiosocket_exec(struct ast_channel *chan, const char *data)
75{
76 char *parse;
77 struct ast_format *readFormat, *writeFormat;
78 const char *chanName;
79 int res;
80
82 AST_APP_ARG(idStr);
83 AST_APP_ARG(server);
84 );
85
86 int s = 0;
87 uuid_t uu;
88
89
90 chanName = ast_channel_name(chan);
91
92 /* Parse and validate arguments */
93 parse = ast_strdupa(data);
95 if (ast_strlen_zero(args.idStr)) {
96 ast_log(LOG_ERROR, "UUID is required\n");
97 return -1;
98 }
99 if (uuid_parse(args.idStr, uu)) {
100 ast_log(LOG_ERROR, "Failed to parse UUID '%s'\n", args.idStr);
101 return -1;
102 }
103 if ((s = ast_audiosocket_connect(args.server, chan)) < 0) {
104 /* The res module will already output a log message, so another is not needed */
105 return -1;
106 }
107
108 writeFormat = ao2_bump(ast_channel_writeformat(chan));
109 readFormat = ao2_bump(ast_channel_readformat(chan));
110
112 ast_log(LOG_ERROR, "Failed to set write format to SLINEAR for channel %s\n", chanName);
113 ao2_ref(writeFormat, -1);
114 ao2_ref(readFormat, -1);
115 close(s);
116 return -1;
117 }
119 ast_log(LOG_ERROR, "Failed to set read format to SLINEAR for channel %s\n", chanName);
120
121 /* Attempt to restore previous write format even though it is likely to
122 * fail, since setting the read format did.
123 */
124 if (ast_set_write_format(chan, writeFormat)) {
125 ast_log(LOG_ERROR, "Failed to restore write format for channel %s\n", chanName);
126 }
127 ao2_ref(writeFormat, -1);
128 ao2_ref(readFormat, -1);
129 close(s);
130 return -1;
131 }
132
133 res = audiosocket_run(chan, args.idStr, s);
134 /* On non-zero return, report failure */
135 if (res) {
136 /* Restore previous formats and close the connection */
137 if (ast_set_write_format(chan, writeFormat)) {
138 ast_log(LOG_ERROR, "Failed to restore write format for channel %s\n", chanName);
139 }
140 if (ast_set_read_format(chan, readFormat)) {
141 ast_log(LOG_ERROR, "Failed to restore read format for channel %s\n", chanName);
142 }
143 ao2_ref(writeFormat, -1);
144 ao2_ref(readFormat, -1);
145 close(s);
146 return res;
147 }
148 close(s);
149
150 if (ast_set_write_format(chan, writeFormat)) {
151 ast_log(LOG_ERROR, "Failed to restore write format for channel %s\n", chanName);
152 }
153 if (ast_set_read_format(chan, readFormat)) {
154 ast_log(LOG_ERROR, "Failed to restore read format for channel %s\n", chanName);
155 }
156 ao2_ref(writeFormat, -1);
157 ao2_ref(readFormat, -1);
158
159 return 0;
160}
161
162static int audiosocket_run(struct ast_channel *chan, const char *id, int svc)
163{
164 const char *chanName;
165 struct ast_channel *targetChan;
166 int ms = 0;
167 int outfd = -1;
168 struct ast_frame *f;
169
170 if (!chan || ast_channel_state(chan) != AST_STATE_UP) {
171 ast_log(LOG_ERROR, "Channel is %s\n", chan ? "not answered" : "missing");
172 return -1;
173 }
174
175 if (ast_audiosocket_init(svc, id)) {
176 ast_log(LOG_ERROR, "Failed to intialize AudioSocket\n");
177 return -1;
178 }
179
180 chanName = ast_channel_name(chan);
181
182 while (1) {
183 ms = -1;
184 targetChan = ast_waitfor_nandfds(&chan, 1, &svc, 1, NULL, &outfd, &ms);
185 if (targetChan) {
186 f = ast_read(chan);
187 if (!f) {
188 return -1;
189 }
190
191 if (f->frametype == AST_FRAME_VOICE) {
192 /* Send audio frame to audiosocket */
193 if (ast_audiosocket_send_frame(svc, f)) {
194 ast_log(LOG_ERROR, "Failed to forward channel frame from %s to AudioSocket\n",
195 chanName);
196 ast_frfree(f);
197 return -1;
198 }
199 }
200 ast_frfree(f);
201 }
202
203 if (outfd >= 0) {
205 if (!f) {
206 ast_log(LOG_ERROR, "Failed to receive frame from AudioSocket message for"
207 "channel %s\n", chanName);
208 return -1;
209 }
210 if (ast_write(chan, f)) {
211 ast_log(LOG_WARNING, "Failed to forward frame to channel %s\n", chanName);
212 ast_frfree(f);
213 return -1;
214 }
215 ast_frfree(f);
216 }
217 }
218 return 0;
219}
220
221static int unload_module(void)
222{
224}
225
226static int load_module(void)
227{
229}
230
234 "AudioSocket Application",
235 .support_level = AST_MODULE_SUPPORT_EXTENDED,
236 .load = load_module,
237 .unload = unload_module,
238 .load_pri = AST_MODPRI_CHANNEL_DRIVER,
239 .requires = "res_audiosocket",
static const char app[]
static int audiosocket_exec(struct ast_channel *chan, const char *data)
static int audiosocket_run(struct ast_channel *chan, const char *id, const int svc)
static int load_module(void)
static int unload_module(void)
Asterisk main include file. File version handling, generic pbx functions.
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_log
Definition: astobj2.c:42
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition: astobj2.h:480
General Asterisk PBX channel definitions.
const char * ast_channel_name(const struct ast_channel *chan)
struct ast_channel * ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds, int nfds, int *exception, int *outfd, int *ms)
Waits for activity on a group of channels.
Definition: channel.c:2988
int ast_write(struct ast_channel *chan, struct ast_frame *frame)
Write a frame to a channel This function writes the given frame to the indicated channel.
Definition: channel.c:5144
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
Definition: channel.c:4257
int ast_set_read_format(struct ast_channel *chan, struct ast_format *format)
Sets read format on channel chan.
Definition: channel.c:5762
struct ast_format * ast_channel_writeformat(struct ast_channel *chan)
int ast_set_write_format(struct ast_channel *chan, struct ast_format *format)
Sets write format on channel chan.
Definition: channel.c:5803
struct ast_format * ast_channel_readformat(struct ast_channel *chan)
ast_channel_state
ast_channel states
Definition: channelstate.h:35
@ AST_STATE_UP
Definition: channelstate.h:42
Generic File Format Support. Should be included by clients of the file handling routines....
Media Format Cache API.
struct ast_format * ast_format_slin
Built-in cached signed linear 8kHz format.
Definition: format_cache.c:41
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
#define AST_APP_ARG(name)
Define an application argument.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
#define ast_frfree(fr)
@ AST_FRAME_VOICE
#define LOG_ERROR
#define LOG_WARNING
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:317
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:543
@ AST_MODPRI_CHANNEL_DRIVER
Definition: module.h:327
@ AST_MODULE_SUPPORT_EXTENDED
Definition: module.h:122
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:626
AudioSocket support functions.
const int ast_audiosocket_init(const int svc, const char *id)
Send the initial message to an AudioSocket server.
const int ast_audiosocket_send_frame(const int svc, const struct ast_frame *f)
Send an Asterisk audio frame to an AudioSocket server.
struct ast_frame * ast_audiosocket_receive_frame(const int svc)
Receive an Asterisk frame from an AudioSocket server.
const int ast_audiosocket_connect(const char *server, struct ast_channel *chan)
Send the initial message to an AudioSocket server.
#define NULL
Definition: resample.c:96
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
Main Channel structure associated with a channel.
Definition of a media format.
Definition: format.c:43
Data structure associated with a single frame of data.
enum ast_frame_type frametype
const char * args
Utility functions.