Asterisk - The Open Source Telephony Project  18.5.0
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 <[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 AudioSocket application -- transmit and receive audio through a TCP socket
22  *
23  * \author Seán C McCord <[email protected]>
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"
43 #include "asterisk/format_cache.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://wiki.asterisk.org/wiki/display/AST/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 
70 static const char app[] = "AudioSocket";
71 
72 static int audiosocket_run(struct ast_channel *chan, const char *id, const int svc);
73 
74 static 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 
162 static 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 
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 
221 static int unload_module(void)
222 {
224 }
225 
226 static 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",
240 );
Main Channel structure associated with a channel.
Asterisk main include file. File version handling, generic pbx functions.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
#define LOG_WARNING
Definition: logger.h:274
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
Definition: channel.c:4302
ast_channel_state
ast_channel states
Definition: channelstate.h:35
Definition of a media format.
Definition: format.c:43
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
const char * args
#define NULL
Definition: resample.c:96
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
struct ast_channel * ast_waitfor_nandfds(struct ast_channel **chan, int n, int *fds, int nfds, int *exception, int *outfd, int *ms)
Waits for activity on a group of channels.
Definition: channel.c:2997
const int ast_audiosocket_connect(const char *server, struct ast_channel *chan)
Send the initial message to an AudioSocket server.
Utility functions.
#define ast_strlen_zero(foo)
Definition: strings.h:52
struct ast_format * ast_channel_readformat(struct ast_channel *chan)
static int load_module(void)
#define ao2_bump(obj)
Definition: astobj2.h:491
#define ast_log
Definition: astobj2.c:42
const int ast_audiosocket_init(const int svc, const char *id)
Send the initial message to an AudioSocket server.
General Asterisk PBX channel definitions.
int ast_set_read_format(struct ast_channel *chan, struct ast_format *format)
Sets read format on channel chan.
Definition: channel.c:5849
#define ao2_ref(o, delta)
Definition: astobj2.h:464
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
AudioSocket support functions.
int ast_set_write_format(struct ast_channel *chan, struct ast_format *format)
Sets write format on channel chan.
Definition: channel.c:5890
#define LOG_ERROR
Definition: logger.h:285
const int ast_audiosocket_send_frame(const int svc, const struct ast_frame *f)
Send an Asterisk audio frame to an AudioSocket server.
static int unload_module(void)
static void parse(struct mgcp_request *req)
Definition: chan_mgcp.c:1872
static const char app[]
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:5189
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS|AST_MODFLAG_LOAD_ORDER, "HTTP Phone Provisioning",.support_level=AST_MODULE_SUPPORT_EXTENDED,.load=load_module,.unload=unload_module,.reload=reload,.load_pri=AST_MODPRI_CHANNEL_DEPEND,.requires="http",)
struct ast_frame * ast_audiosocket_receive_frame(const int svc)
Receive an Asterisk frame from an AudioSocket server.
const char * ast_channel_name(const struct ast_channel *chan)
#define ast_frfree(fr)
Data structure associated with a single frame of data.
union ast_frame::@263 data
enum ast_frame_type frametype
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
struct ast_format * ast_format_slin
Built-in cached signed linear 8kHz format.
Definition: format_cache.c:41
Asterisk module definitions.
struct ast_format * ast_channel_writeformat(struct ast_channel *chan)
static int audiosocket_exec(struct ast_channel *chan, const char *data)
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
static int audiosocket_run(struct ast_channel *chan, const char *id, const int svc)
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:626
Media Format Cache API.
#define AST_APP_ARG(name)
Define an application argument.